From a4dc7cf2f14ed469fd17a4c1cb3ea8eec5ff7d72 Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Thu, 30 Jun 2016 19:15:13 +0800 Subject: [PATCH 01/30] add type --- src/std/database/front.d | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/std/database/front.d b/src/std/database/front.d index ca72e6c..314cf8b 100644 --- a/src/std/database/front.d +++ b/src/std/database/front.d @@ -34,6 +34,8 @@ enum ValueType { Int, String, Date, + DateTime, + Double, Variant, } From 601bb38c03c944aa6865e0e2c096207dead9881e Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Fri, 1 Jul 2016 20:21:01 +0800 Subject: [PATCH 02/30] has over when free ruset --- .directory | 6 + .kdev4/dstddb.kdev4 | 23 + .project | 18 + CMakeLists.txt | 3 + dstddb.cmake | 44 ++ dstddb.kdev4 | 3 + dub.json.user | 186 ++++++ dub.userprefs | 8 + src/std/database/allocator.d | 6 +- src/std/database/freetds/database.d | 7 + src/std/database/front.d | 67 +- src/std/database/mysql/database.d | 877 +++++++++++++++++++-------- src/std/database/odbc/database.d | 7 + src/std/database/oracle/database.d | 8 + src/std/database/poly/database.d | 6 + src/std/database/postgres/database.d | 8 + src/std/database/sqlite/database.d | 6 + 17 files changed, 1021 insertions(+), 262 deletions(-) create mode 100644 .directory create mode 100644 .kdev4/dstddb.kdev4 create mode 100644 .project create mode 100644 CMakeLists.txt create mode 100644 dstddb.cmake create mode 100644 dstddb.kdev4 create mode 100644 dub.json.user create mode 100644 dub.userprefs diff --git a/.directory b/.directory new file mode 100644 index 0000000..10eae53 --- /dev/null +++ b/.directory @@ -0,0 +1,6 @@ +[Dolphin] +Timestamp=2016,6,3,17,52,40 +Version=3 + +[Settings] +HiddenFilesShown=true diff --git a/.kdev4/dstddb.kdev4 b/.kdev4/dstddb.kdev4 new file mode 100644 index 0000000..7c942bc --- /dev/null +++ b/.kdev4/dstddb.kdev4 @@ -0,0 +1,23 @@ +[Buildset] +BuildItems=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x01\x00\x00\x00\x0b\x00\x00\x00\x00\x01\x00\x00\x00\x0c\x00d\x00s\x00t\x00d\x00d\x00b) + +[CMake] +Build Directory Count=1 +Current Build Directory Index=0 +ProjectRootRelative=./ + +[CMake][CMake Build Directory 0] +Build Directory Path=file:///home/dsby/code/dlang/github/dstddb/build +Build Type=Debug +CMake Binary=file:///usr/bin/cmake +Environment Profile= +Extra Arguments= +Install Directory= + +[Defines And Includes][Compiler] +Name=GCC +Path=gcc +Type=GCC + +[Project] +VersionControlSupport=kdevgit diff --git a/.project b/.project new file mode 100644 index 0000000..cf2333b --- /dev/null +++ b/.project @@ -0,0 +1,18 @@ + + + dstddb + + + + + + org.dsource.ddt.ide.core.DubBuilder + clean,full,incremental, + + + + + + org.dsource.ddt.ide.core.nature + + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0f8236a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.0) +project(dstddb D) +include(/home/dsby/code/dlang/github/dstddb/dstddb.cmake) diff --git a/dstddb.cmake b/dstddb.cmake new file mode 100644 index 0000000..76e7d7e --- /dev/null +++ b/dstddb.cmake @@ -0,0 +1,44 @@ +include(UseD) +add_d_conditions(VERSION Have_dstddb DEBUG ) +include_directories(/home/dsby/code/dlang/github/dstddb/src/) +add_library(dstddb + /home/dsby/code/dlang/github/dstddb/src/std/database/allocator.d + /home/dsby/code/dlang/github/dstddb/src/std/database/array.d + /home/dsby/code/dlang/github/dstddb/src/std/database/common.d + /home/dsby/code/dlang/github/dstddb/src/std/database/exception.d + /home/dsby/code/dlang/github/dstddb/src/std/database/freetds/bindings.d + /home/dsby/code/dlang/github/dstddb/src/std/database/freetds/database.d + /home/dsby/code/dlang/github/dstddb/src/std/database/freetds/package.d + /home/dsby/code/dlang/github/dstddb/src/std/database/front.d + /home/dsby/code/dlang/github/dstddb/src/std/database/mysql/bindings.d + /home/dsby/code/dlang/github/dstddb/src/std/database/mysql/database.d + /home/dsby/code/dlang/github/dstddb/src/std/database/mysql/package.d + /home/dsby/code/dlang/github/dstddb/src/std/database/odbc/database.d + /home/dsby/code/dlang/github/dstddb/src/std/database/odbc/package.d + /home/dsby/code/dlang/github/dstddb/src/std/database/option.d + /home/dsby/code/dlang/github/dstddb/src/std/database/oracle/bindings.d + /home/dsby/code/dlang/github/dstddb/src/std/database/oracle/database.d + /home/dsby/code/dlang/github/dstddb/src/std/database/oracle/package.d + /home/dsby/code/dlang/github/dstddb/src/std/database/oracle/stubs.d + /home/dsby/code/dlang/github/dstddb/src/std/database/package.d + /home/dsby/code/dlang/github/dstddb/src/std/database/poly/database.d + /home/dsby/code/dlang/github/dstddb/src/std/database/poly/package.d + /home/dsby/code/dlang/github/dstddb/src/std/database/pool.d + /home/dsby/code/dlang/github/dstddb/src/std/database/postgres/bindings.d + /home/dsby/code/dlang/github/dstddb/src/std/database/postgres/database.d + /home/dsby/code/dlang/github/dstddb/src/std/database/postgres/package.d + /home/dsby/code/dlang/github/dstddb/src/std/database/reference/database.d + /home/dsby/code/dlang/github/dstddb/src/std/database/reference/package.d + /home/dsby/code/dlang/github/dstddb/src/std/database/resolver.d + /home/dsby/code/dlang/github/dstddb/src/std/database/rowset.d + /home/dsby/code/dlang/github/dstddb/src/std/database/source.d + /home/dsby/code/dlang/github/dstddb/src/std/database/sqlite/database.d + /home/dsby/code/dlang/github/dstddb/src/std/database/sqlite/package.d + /home/dsby/code/dlang/github/dstddb/src/std/database/testsuite.d + /home/dsby/code/dlang/github/dstddb/src/std/database/uri.d + /home/dsby/code/dlang/github/dstddb/src/std/database/util.d + /home/dsby/code/dlang/github/dstddb/src/std/database/variant.d + /home/dsby/code/dlang/github/dstddb/src/std/database/vibehandler.d +) +target_link_libraries(dstddb ) +set_target_properties(dstddb PROPERTIES TEXT_INCLUDE_DIRECTORIES "") diff --git a/dstddb.kdev4 b/dstddb.kdev4 new file mode 100644 index 0000000..f5fe523 --- /dev/null +++ b/dstddb.kdev4 @@ -0,0 +1,3 @@ +[Project] +Manager=KDevCMakeManager +Name=dstddb diff --git a/dub.json.user b/dub.json.user new file mode 100644 index 0000000..6772844 --- /dev/null +++ b/dub.json.user @@ -0,0 +1,186 @@ + + + + + + DubProjectManager.DubProject.SourceTreeConfiguration + <no config> + + + EnvironmentId + {afc4a408-be65-4fd8-b2d3-bf11fa3941f2} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + true + false + 0 + true + 0 + 8 + true + 1 + true + true + true + false + + + + ProjectExplorer.Project.PluginSettings + + + + 1 + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + {b3466256-b4e1-4e5f-afc6-26e0639d4f75} + 0 + 0 + 0 + + /home/dsby/code/dlang/github/dstddb + + + + debug + <no config> + + true + + Dub + DubProjectManager.BuildStep + + 1 + 构建 + + ProjectExplorer.BuildSteps.Build + + + 0 + 清理 + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + all + all + DubProjectManager.DubBuildConfiguration + + 1 + + + 0 + 部署 + + ProjectExplorer.BuildSteps.Deploy + + 1 + 在本地部署 + + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + + false + false + false + false + true + 0.01 + 10 + true + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + + <no config> + 2 + + dstddb + dstddb + DubProjectManager.RunConfigurationdstddb + 3768 + false + true + false + false + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 18 + + + Version + 18 + + diff --git a/dub.userprefs b/dub.userprefs new file mode 100644 index 0000000..0f08895 --- /dev/null +++ b/dub.userprefs @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/std/database/allocator.d b/src/std/database/allocator.d index a498f4c..a732e45 100644 --- a/src/std/database/allocator.d +++ b/src/std/database/allocator.d @@ -9,7 +9,8 @@ struct MyMallocator { void[] allocate(size_t bytes) { import core.stdc.stdlib : malloc; if (!bytes) return null; - auto p = malloc(bytes); + auto p = malloc(bytes + 1); + log("allocate: ptr: ", p , " size :",bytes); return p ? p[0 .. bytes] : null; } @@ -17,8 +18,9 @@ struct MyMallocator { @system // removed @nogc and nothrow for logging bool deallocate(void[] b) { import core.stdc.stdlib : free; - //log("deallocate: ptr: ", b.ptr, "size: ", b.length); + log("deallocate: ptr: ", b.ptr, " size: ", b.length); free(b.ptr); + log("return true"); return true; } diff --git a/src/std/database/freetds/database.d b/src/std/database/freetds/database.d index 5840231..79659a0 100644 --- a/src/std/database/freetds/database.d +++ b/src/std/database/freetds/database.d @@ -16,6 +16,7 @@ import std.experimental.logger; public import std.database.allocator; import std.database.front; import std.datetime; +import std.variant; struct DefaultPolicy { alias Allocator = MyMallocator; @@ -339,6 +340,12 @@ struct Driver(Policy) { return to!string(describe[idx].name); } + ubyte[] rawData(Cell* cell) { //TODO: fix + return null; + } + + bool isNull(Cell* cell){return false;} + auto get(X:string)(Cell* cell) { import core.stdc.string: strlen; checkType(cell.bind.bindType, NTBSTRINGBIND); diff --git a/src/std/database/front.d b/src/std/database/front.d index 314cf8b..f6de93a 100644 --- a/src/std/database/front.d +++ b/src/std/database/front.d @@ -12,7 +12,7 @@ import std.database.source; import std.database.allocator; import std.traits; import std.container.array; -import std.variant; +public import std.variant; import std.range.primitives; import std.database.option; @@ -29,14 +29,38 @@ public import std.database.array; (version_major == 2 && version_minor == 70)); */ +struct Time +{ + this(uint h, uint m, uint s = 0, uint ms = 0) + { + hour = h; minute = m; second = s; msecond = ms; + } + + uint hour; + uint minute; + uint second; + uint msecond; +} enum ValueType { - Int, + Char, + + Short, + Int, + Long, + + Float, + Double, + String, + Date, + Time, DateTime, - Double, - Variant, + + Raw, + + Variant //TODO: remove } // improve @@ -412,7 +436,7 @@ struct BasicColumn(D,P) { } auto idx() {return idx_;} - auto name() {return "abc";} + auto name() {return result_.data_.name(idx_);} } @@ -520,8 +544,8 @@ struct BasicRow(D,P) { auto result = &rows_.result_; // needs work // sending a ptr to cell instead of reference (dangerous) - auto cell = Cell(result, &result.data_.bind[idx], idx); - return Value(result, cell); + //auto cell = Cell(result, &result.data_.bind[idx], idx); + return Value(Cell(result, &result.data_.bind[idx], idx)); } } @@ -533,20 +557,23 @@ struct BasicValue(D,P) { alias Result = BasicResult!(Driver,Policy); alias Cell = BasicCell!(Driver,Policy); alias Bind = Driver.Bind; - private Result* result_; - private Bind* bind_; + // private Result* result_; + // private Bind* bind_; private Cell cell_; + private Variant data_; alias Converter = .Converter!(Driver,Policy); - this(Result* result, Cell cell) { - result_ = result; + this(/*Result* result,*/ Cell cell) { + // result_ = result; //bind_ = bind; cell_ = cell; } private auto resultPtr() { - return &result_.result(); // last parens matter here (something about delegate) + auto r = cell_.result_; + return &(r.result()); // last parens matter here (something about delegate) } + ubyte[] rawData(){return resultPtr.rawData(&cell_);} auto type() {return cell_.bind.type;} @@ -558,7 +585,7 @@ struct BasicValue(D,P) { // should be nothrow? auto as(T:Variant)() {return Converter.convert!T(resultPtr, cell_);} - bool isNull() {return false;} //fix + bool isNull() {return resultPtr.isNull(&cell_);} //fix string name() {return resultPtr.name(cell_.idx_);} @@ -575,9 +602,10 @@ struct BasicValue(D,P) { } -// extra stuff +// extra stuff +/* struct EfficientValue(T) { alias Driver = T.Driver; alias Bind = Driver.Bind; @@ -592,23 +620,32 @@ struct EfficientValue(T) { auto as(T:Variant)() {return Converter.convertDirect!T(bind_);} } - +*/ struct BasicCell(D,P) { alias Driver = D; alias Policy = P; alias Result = BasicResult!(Driver,Policy); alias Bind = Driver.Bind; + alias Value = BasicValue!(Driver,Policy); private Bind* bind_; private int rowIdx_; private size_t idx_; + private Result * result_; this(Result *r, Bind *b, size_t idx) { bind_ = b; + result_ = r; rowIdx_ = r.rowIdx_; idx_ = idx; } + private auto resultPtr() { + return &result_.result(); // last parens matter here (something about delegate) + } + + auto value(){return Value(this);} + auto bind() {return bind_;} auto rowIdx() {return rowIdx_;} } diff --git a/src/std/database/mysql/database.d b/src/std/database/mysql/database.d index 7bccf4a..902d72f 100644 --- a/src/std/database/mysql/database.d +++ b/src/std/database/mysql/database.d @@ -7,12 +7,18 @@ import std.stdio; import std.database.common; import std.database.source; -version(Windows) { +version (Windows) +{ pragma(lib, "libmysql"); -} else { - version (webscalesql) { +} +else +{ + version (webscalesql) + { pragma(lib, "webscalesql"); - } else { + } + else + { pragma(lib, "mysqlclient"); } } @@ -27,65 +33,76 @@ import std.string; // alias Database(T) = std.database.impl.Database!(T,DatabaseImpl!T); -alias Database(T) = BasicDatabase!(Driver!T.Sync,T); - -alias AsyncDatabase(T) = BasicDatabase!(Driver!T.Async,T); +alias Database(T) = BasicDatabase!(Driver!T.Sync, T); +alias AsyncDatabase(T) = BasicDatabase!(Driver!T.Async, T); -struct DefaultPolicy { +struct DefaultPolicy +{ alias Allocator = MyMallocator; } -auto createDatabase()(string uri="") { - return Database!DefaultPolicy(uri); +auto createDatabase()(string uri = "") +{ + return Database!DefaultPolicy(uri); } -auto createDatabase(T)(string uri="") { - return Database!T(uri); +auto createDatabase(T)(string uri = "") +{ + return Database!T(uri); } -private static bool isError()(int ret) { - return - !(ret == 0 || - ret == MYSQL_NO_DATA || - ret == MYSQL_DATA_TRUNCATED); +private static bool isError()(int ret) +{ + return !(ret == 0 || ret == MYSQL_NO_DATA || ret == MYSQL_DATA_TRUNCATED); } -private static T* check(T)(string msg, T* ptr) { +private static T* check(T)(string msg, T* ptr) +{ info(msg, ":", ptr); - if (!ptr) raiseError(msg); + if (!ptr) + raiseError(msg); return ptr; } -private static int check()(string msg, MYSQL_STMT* stmt, int ret) { +private static int check()(string msg, MYSQL_STMT* stmt, int ret) +{ info(msg, ":", ret); - if (isError(ret)) raiseError(msg,stmt,ret); + if (isError(ret)) + raiseError(msg, stmt, ret); return ret; } -private static void raiseError()(string msg) { +private static void raiseError()(string msg) +{ throw new DatabaseException("mysql error: " ~ msg); } -private static void raiseError()(string msg, int ret) { +private static void raiseError()(string msg, int ret) +{ throw new DatabaseException("mysql error: status: " ~ to!string(ret) ~ ":" ~ msg); } -private static void raiseError()(string msg, MYSQL_STMT* stmt, int ret) { - import core.stdc.string: strlen; +private static void raiseError()(string msg, MYSQL_STMT* stmt, int ret) +{ + import core.stdc.string : strlen; + const(char*) err = mysql_stmt_error(stmt); throw new DatabaseException("mysql error: " ~ msg); } -private struct Driver(Policy) { +private struct Driver(Policy) +{ - struct Describe { + struct Describe + { int index; immutable(char)[] name; - MYSQL_FIELD *field; + MYSQL_FIELD* field; } - struct Bind { + struct Bind + { ValueType type; int mysql_type; int allocSize; @@ -95,67 +112,72 @@ private struct Driver(Policy) { my_bool error; } - - struct Sync { + struct Sync + { alias Allocator = Policy.Allocator; - alias Cell = BasicCell!(Sync,Policy); + alias Cell = BasicCell!(Sync, Policy); alias const(ubyte)* cstring; alias Describe = Driver!Policy.Describe; alias Bind = Driver!Policy.Bind; - struct Database { + struct Database + { alias queryVariableType = QueryVariableType.QuestionMark; - static const FeatureArray features = [ - Feature.InputBinding, - Feature.DateBinding, + static const FeatureArray features = [Feature.InputBinding, Feature.DateBinding, //Feature.ConnectionPool, ]; Allocator allocator; - this(string uri) { + this(string uri) + { allocator = Allocator(); info("mysql client info: ", to!string(mysql_get_client_info())); } - //~this() {log("~Database");} + ~this() + { + log("~Database"); + } } - struct Connection { - Database *db; - MYSQL *mysql; + struct Connection + { + Database* db; + MYSQL* mysql; - this(Database *db_, Source source) { + this(Database* db_, Source source) + { db = db_; mysql = check("mysql_init", mysql_init(null)); - check("mysql_real_connect", mysql_real_connect( - mysql, - cast(cstring) toStringz(source.server), - cast(cstring) toStringz(source.username), - cast(cstring) toStringz(source.password), - cast(cstring) toStringz(source.database), - 0, - null, - 0)); + check("mysql_real_connect", mysql_real_connect(mysql, + cast(cstring) toStringz(source.server), + cast(cstring) toStringz(source.username), + cast(cstring) toStringz(source.password), + cast(cstring) toStringz(source.database), 0, null, 0)); } - ~this() { - //log("~Statement"); - if (mysql) mysql_close(mysql); + ~this() + { + log("~Statement"); + if (mysql) + mysql_close(mysql); mysql = null; } } - static void bindSetup(ref Array!Bind bind, ref Array!MYSQL_BIND mysqlBind) { + static void bindSetup(ref Array!Bind bind, ref Array!MYSQL_BIND mysqlBind) + { // make this efficient mysqlBind.clear(); mysqlBind.reserve(bind.length); - for(int i=0; i!=bind.length; ++i) { + for (int i = 0; i != bind.length; ++i) + { mysqlBind ~= MYSQL_BIND(); //import core.stdc.string: memset; //memset(mb, 0, MYSQL_BIND.sizeof); // might not be needed: D struct @@ -170,74 +192,94 @@ private struct Driver(Policy) { } } - - struct Statement { - Connection *con; + struct Statement + { + Connection* con; string sql; - Allocator *allocator; - MYSQL_STMT *stmt; + Allocator* allocator; + MYSQL_STMT* stmt; uint binds; Array!Bind inputBind; Array!MYSQL_BIND mysqlBind; bool bindInit; - this(Connection *con_, string sql_) { + this(Connection* con_, string sql_) + { con = con_; sql = sql_; allocator = &con.db.allocator; stmt = check("mysql_stmt_init", mysql_stmt_init(con.mysql)); } - ~this() { - foreach(b; inputBind) allocator.deallocate(b.data); - if (stmt) mysql_stmt_close(stmt); + ~this() + { + log("~Statement"); + foreach (b; inputBind) + allocator.deallocate(b.data); + if (stmt) + mysql_stmt_close(stmt); // stmt = null? needed } // hoist? - this(this) { assert(false); } - void opAssign(Statement rhs) { assert(false); } + this(this) + { + assert(false); + } - void prepare() { - check("mysql_stmt_prepare", stmt, mysql_stmt_prepare( - stmt, - cast(char*) sql.ptr, - sql.length)); + void opAssign(Statement rhs) + { + assert(false); + } + + void prepare() + { + check("mysql_stmt_prepare", stmt, mysql_stmt_prepare(stmt, + cast(char*) sql.ptr, sql.length)); binds = cast(uint) mysql_stmt_param_count(stmt); } - void query() { - if (inputBind.length && !bindInit) { + void query() + { + if (inputBind.length && !bindInit) + { bindInit = true; bindSetup(inputBind, mysqlBind); - check("mysql_stmt_bind_param", - stmt, - mysql_stmt_bind_param(stmt, &mysqlBind[0])); + check("mysql_stmt_bind_param", stmt, + mysql_stmt_bind_param(stmt, &mysqlBind[0])); } info("execute: ", sql); check("mysql_stmt_execute", stmt, mysql_stmt_execute(stmt)); } - void query(X...) (X args) { + void query(X...)(X args) + { bindAll(args); query(); } - bool hasRows() {return true;} + bool hasRows() + { + return true; + } - void reset() { + void reset() + { } - private void bindAll(T...) (T args) { + private void bindAll(T...)(T args) + { int col; - foreach (arg; args) bind(++col, arg); + foreach (arg; args) + bind(++col, arg); } - void bind(int n, int value) { + void bind(int n, int value) + { info("input bind: n: ", n, ", value: ", value); auto b = bindAlloc(n, MYSQL_TYPE_LONG, int.sizeof); b.is_null = 0; @@ -246,11 +288,13 @@ private struct Driver(Policy) { } - void bind(int n, const char[] value){ - import core.stdc.string: strncpy; + void bind(int n, const char[] value) + { + import core.stdc.string : strncpy; + info("input bind: n: ", n, ", value: ", value); - auto b = bindAlloc(n, MYSQL_TYPE_STRING, 100+1); // fix + auto b = bindAlloc(n, MYSQL_TYPE_STRING, 100 + 1); // fix b.is_null = 0; b.error = 0; @@ -260,7 +304,8 @@ private struct Driver(Policy) { b.length = value.length; } - void bind(int n, Date d) { + void bind(int n, Date d) + { auto b = bindAlloc(n, MYSQL_TYPE_DATE, MYSQL_TIME.sizeof); b.is_null = 0; b.error = 0; @@ -271,13 +316,18 @@ private struct Driver(Policy) { p.day = d.day; } - Bind* bindAlloc(int n, int mysql_type, int allocSize) { - if (n==0) throw new DatabaseException("zero index"); - auto idx = n-1; - if (idx > inputBind.length) throw new DatabaseException("bind range error"); - if (idx == inputBind.length) inputBind ~= Bind(); + Bind* bindAlloc(int n, int mysql_type, int allocSize) + { + if (n == 0) + throw new DatabaseException("zero index"); + auto idx = n - 1; + if (idx > inputBind.length) + throw new DatabaseException("bind range error"); + if (idx == inputBind.length) + inputBind ~= Bind(); auto b = &inputBind[idx]; - if (allocSize <= b.data.length) return b; // fix + if (allocSize <= b.data.length) + return b; // fix b.mysql_type = mysql_type; b.allocSize = allocSize; b.data = allocator.allocate(b.allocSize); @@ -286,44 +336,53 @@ private struct Driver(Policy) { } - struct Result { - Statement *stmt; - Allocator *allocator; + struct Result + { + Statement* stmt; + Allocator* allocator; uint columns; Array!Describe describe; Array!Bind bind; Array!MYSQL_BIND mysqlBind; - MYSQL_RES *result_metadata; + MYSQL_RES* result_metadata; int status; static const maxData = 256; - this(Statement* stmt_, int rowArraySize_) { + this(Statement* stmt_, int rowArraySize_) + { stmt = stmt_; allocator = stmt.allocator; result_metadata = mysql_stmt_result_metadata(stmt.stmt); - if (!result_metadata) return; + if (!result_metadata) + return; //columns = mysql_num_fields(result_metadata); build_describe(); build_bind(); } - ~this() { - //log("~Result"); - foreach(b; bind) allocator.deallocate(b.data); - if (result_metadata) mysql_free_result(result_metadata); + ~this() + { + log("~Result"); + foreach (b; bind) + allocator.deallocate(b.data); + if (result_metadata) + mysql_free_result(result_metadata); + log("~Result"); } - void build_describe() { - import core.stdc.string: strlen; + void build_describe() + { + import core.stdc.string : strlen; columns = cast(uint) mysql_stmt_field_count(stmt.stmt); describe.reserve(columns); - for(int i = 0; i != columns; ++i) { + for (int i = 0; i != columns; ++i) + { describe ~= Describe(); auto d = &describe.back(); @@ -337,19 +396,65 @@ private struct Driver(Policy) { } } - void build_bind() { - import core.stdc.string: memset; + void build_bind() + { + import core.stdc.string : memset; import core.memory : GC; bind.reserve(columns); - for(int i = 0; i != columns; ++i) { + for (int i = 0; i != columns; ++i) + { auto d = &describe[i]; bind ~= Bind(); auto b = &bind.back(); + b.mysql_type = d.field.type; + switch (d.field.type) + { + case MYSQL_TYPE_TINY: + b.type = ValueType.Char; + break; + case MYSQL_TYPE_SHORT: + b.type = ValueType.Short; + break; + /* case MYSQL_TYPE_INT24: + b.mysql_type = MYSQL_TYPE_STRING; + b.type = ValueType. + goto case;*/ + case MYSQL_TYPE_LONG: + b.type = ValueType.Int; + break; + case MYSQL_TYPE_LONGLONG: + b.type = ValueType.Long; + break; + case MYSQL_TYPE_FLOAT: + b.type = ValueType.Float; + break; + case MYSQL_TYPE_DOUBLE: + b.type = ValueType.Double; + break; + + case MYSQL_TYPE_DATE: + b.type = ValueType.Date; + break; + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_TIME2: + b.type = ValueType.Time; + break; + case MYSQL_TYPE_DATETIME2: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP2: + case MYSQL_TYPE_TIMESTAMP: + b.type = ValueType.DateTime; + break; + default: + b.mysql_type = MYSQL_TYPE_STRING; + b.type = ValueType.String; + break; + } // let in ints for now - if (d.field.type == MYSQL_TYPE_LONG) { + /* if (d.field.type == MYSQL_TYPE_LONG) { b.mysql_type = d.field.type; b.type = ValueType.Int; } else if (d.field.type == MYSQL_TYPE_DATE) { @@ -358,7 +463,7 @@ private struct Driver(Policy) { } else { b.mysql_type = MYSQL_TYPE_STRING; b.type = ValueType.String; - } + }*/ b.allocSize = cast(uint)(d.field.length + 1); b.data = allocator.allocate(b.allocSize); @@ -369,16 +474,25 @@ private struct Driver(Policy) { mysql_stmt_bind_result(stmt.stmt, &mysqlBind.front()); } - bool hasResult() {return result_metadata != null;} + bool hasResult() + { + return result_metadata != null; + } - int fetch() { + int fetch() + { status = check("mysql_stmt_fetch", stmt.stmt, mysql_stmt_fetch(stmt.stmt)); - if (!status) { + if (!status) + { return 1; - } else if (status == MYSQL_NO_DATA) { + } + else if (status == MYSQL_NO_DATA) + { //rows_ = row_count_; return 0; - } else if (status == MYSQL_DATA_TRUNCATED) { + } + else if (status == MYSQL_DATA_TRUNCATED) + { raiseError("mysql_stmt_fetch: truncation", status); } @@ -388,108 +502,226 @@ private struct Driver(Policy) { // value getters + Variant getValue(Cell* cell) + { + Variant value; + switch (cell.bind.type) + { + case ValueType.Char: + { + auto t = (*cast(char*) cell.bind.data.ptr); + value = t; + } + break; + case ValueType.Short: + { + auto t = (*cast(short*) cell.bind.data.ptr); + value = t; + + } + break; + case ValueType.Int: + { + auto t = (*cast(int*) cell.bind.data.ptr); + value = t; + } + break; + case ValueType.Long: + { + auto t = (*cast(long*) cell.bind.data.ptr); + value = t; + } + break; + case ValueType.Float: + { + auto t = (*cast(float*) cell.bind.data.ptr); + value = t; + } + break; + case ValueType.Double: + { + auto t = (*cast(double*) cell.bind.data.ptr); + value = t; + } + break; + case ValueType.String: + { + auto ptr = cast(char*) cell.bind.data.ptr; + value = cast(string)(ptr[0 .. cell.bind.length]); + } + break; + case ValueType.Date: + { + MYSQL_TIME* t = cast(MYSQL_TIME*) cell.bind.data.ptr; + value = Date(t.year, t.month, t.day); + } + break; + case ValueType.Time: + { + MYSQL_TIME* t = cast(MYSQL_TIME*) cell.bind.data.ptr; + value = Time(t.hour, t.minute, t.second); + } + break; + case ValueType.DateTime: + { + MYSQL_TIME* t = cast(MYSQL_TIME*) cell.bind.data.ptr; + value = DateTime(t.year, t.month, t.day, t.hour, t.minute, + t.second); + } + break; + default: + break; + } + return value; + } + /* char[] get(X:char[])(Bind *b) { auto ptr = cast(char*) b.data.ptr; return ptr[0..b.length]; } */ + ubyte[] rawData(Cell* cell) + { + auto ptr = cast(ubyte*) cell.bind.data.ptr; + return ptr[0 .. cell.bind.length]; + } + + bool isNull(Cell* cell) + { + return cell.bind.is_null > 0; + } - auto name(size_t idx) { + auto name(size_t idx) + { return describe[idx].name; } - auto get(X:string)(Cell* cell) { - //return cast(string) get!(char[])(b); - auto ptr = cast(char*) cell.bind.data.ptr; - return ptr[0..cell.bind.length]; + auto get(X : string)(Cell* cell) + { + writeln("tostring``````````"); + return getValue(cell).toString(); } - auto get(X:int)(Cell* cell) { - return *cast(int*) cell.bind.data.ptr; + auto get(X : int)(Cell* cell) + { + return getValue(cell).get!X(); } - auto get(X:Date)(Cell* cell) { - //return Date(2016,1,1); // fix - MYSQL_TIME *t = cast(MYSQL_TIME*) cell.bind.data.ptr; - //t.year,t.month,t.day,t.hour,t.minute,t.second - return Date(t.year,t.month,t.day); + auto get(X : Date)(Cell* cell) + { + return getValue(cell).get!X(); } - static void checkType(T)(Bind *b) { + static void checkType(T)(Bind* b) + { int x = TypeInfo!T.type(); int y = b.mysql_type; - if (x == y) return; - warning("type pair mismatch: ",x, ":", y); + if (x == y) + return; + warning("type pair mismatch: ", x, ":", y); throw new DatabaseException("type mismatch"); } // refactor as a better 1-n bind mapping - struct TypeInfo(T:int) {static int type() {return MYSQL_TYPE_LONG;}} - struct TypeInfo(T:string) {static int type() {return MYSQL_TYPE_STRING;}} - struct TypeInfo(T:Date) {static int type() {return MYSQL_TYPE_DATE;}} + struct TypeInfo(T : int) + { + static int type() + { + return MYSQL_TYPE_LONG; + } + } + + struct TypeInfo(T : string) + { + static int type() + { + return MYSQL_TYPE_STRING; + } + } + + struct TypeInfo(T : Date) + { + static int type() + { + return MYSQL_TYPE_DATE; + } + } } } - struct Async { + struct Async + { alias Allocator = Policy.Allocator; alias Describe = Driver!Policy.Describe; alias Bind = Driver!Policy.Bind; - alias Cell = BasicCell!(Async,Policy); + alias Cell = BasicCell!(Async, Policy); - struct Database { + struct Database + { alias queryVariableType = QueryVariableType.QuestionMark; - static const FeatureArray features = [ - Feature.InputBinding, - Feature.DateBinding, + static const FeatureArray features = [Feature.InputBinding, Feature.DateBinding, //Feature.ConnectionPool, ]; - this(string defaultURI) { + this(string defaultURI) + { info("mysql client info: ", to!string(mysql_get_client_info())); } - bool bindable() {return true;} - bool dateBinding() {return true;} - bool poolEnable() {return false;} + bool bindable() + { + return true; + } + + bool dateBinding() + { + return true; + } + + bool poolEnable() + { + return false; + } } - struct Connection { - Database *db; - MYSQL *mysql; + struct Connection + { + Database* db; + MYSQL* mysql; - this(Database *db_, Source source) { + this(Database* db_, Source source) + { db = db_; mysql = check("mysql_init", mysql_init(null)); - check("mysql_real_connect", mysql_real_connect( - mysql, - cast(cstring) toStringz(source.server), - cast(cstring) toStringz(source.username), - cast(cstring) toStringz(source.password), - cast(cstring) toStringz(source.database), - 0, - null, - 0)); + check("mysql_real_connect", mysql_real_connect(mysql, + cast(cstring) toStringz(source.server), + cast(cstring) toStringz(source.username), + cast(cstring) toStringz(source.password), + cast(cstring) toStringz(source.database), 0, null, 0)); } - ~this() { - //log("~Statement"); - if (mysql) mysql_close(mysql); + ~this() + { + log("~Statement"); + if (mysql) + mysql_close(mysql); mysql = null; } } - - static void bindSetup(ref Array!Bind bind, ref Array!MYSQL_BIND mysqlBind) { + static void bindSetup(ref Array!Bind bind, ref Array!MYSQL_BIND mysqlBind) + { // make this efficient mysqlBind.clear(); mysqlBind.reserve(bind.length); - for(int i=0; i!=bind.length; ++i) { + for (int i = 0; i != bind.length; ++i) + { mysqlBind ~= MYSQL_BIND(); //import core.stdc.string: memset; //memset(mb, 0, MYSQL_BIND.sizeof); // might not be needed: D struct @@ -504,75 +736,95 @@ private struct Driver(Policy) { } } - - struct Statement { - Connection *con; + struct Statement + { + Connection* con; string sql; Allocator allocator; - MYSQL_STMT *stmt; + MYSQL_STMT* stmt; uint binds; Array!Bind inputBind; Array!MYSQL_BIND mysqlBind; bool bindInit; - this(Connection *con_, string sql_) { + this(Connection* con_, string sql_) + { con = con_; sql = sql_; //allocator = &con.db.allocator; stmt = mysql_stmt_init(con.mysql); - if (!stmt) throw new DatabaseException("stmt error"); + if (!stmt) + throw new DatabaseException("stmt error"); } - ~this() { - foreach(b; inputBind) allocator.deallocate(b.data); - if (stmt) mysql_stmt_close(stmt); + ~this() + { + foreach (b; inputBind) + allocator.deallocate(b.data); + if (stmt) + mysql_stmt_close(stmt); // stmt = null? needed } // hoist? - this(this) { assert(false); } - void opAssign(Statement rhs) { assert(false); } + this(this) + { + assert(false); + } + + void opAssign(Statement rhs) + { + assert(false); + } - void prepare() { - check("mysql_stmt_prepare", stmt, mysql_stmt_prepare( - stmt, - cast(char*) sql.ptr, - sql.length)); + void prepare() + { + check("mysql_stmt_prepare", stmt, mysql_stmt_prepare(stmt, + cast(char*) sql.ptr, sql.length)); binds = cast(uint) mysql_stmt_param_count(stmt); } - void query() { - if (inputBind.length && !bindInit) { + void query() + { + if (inputBind.length && !bindInit) + { bindInit = true; bindSetup(inputBind, mysqlBind); - check("mysql_stmt_bind_param", - stmt, - mysql_stmt_bind_param(stmt, &mysqlBind[0])); + check("mysql_stmt_bind_param", stmt, + mysql_stmt_bind_param(stmt, &mysqlBind[0])); } info("execute: ", sql); check("mysql_stmt_execute", stmt, mysql_stmt_execute(stmt)); } - void query(X...) (X args) { + void query(X...)(X args) + { bindAll(args); query(); } - bool hasRows() {return true;} + bool hasRows() + { + return true; + } - void reset() { + void reset() + { } - private void bindAll(T...) (T args) { + private void bindAll(T...)(T args) + { int col; - foreach (arg; args) bind(++col, arg); + foreach (arg; args) + bind(++col, arg); } - void bind(int n, int value) { + void bind(int n, int value) + { info("input bind: n: ", n, ", value: ", value); auto b = bindAlloc(n, MYSQL_TYPE_LONG, int.sizeof); b.is_null = 0; @@ -581,11 +833,13 @@ private struct Driver(Policy) { } - void bind(int n, const char[] value){ - import core.stdc.string: strncpy; + void bind(int n, const char[] value) + { + import core.stdc.string : strncpy; + info("input bind: n: ", n, ", value: ", value); - auto b = bindAlloc(n, MYSQL_TYPE_STRING, 100+1); // fix + auto b = bindAlloc(n, MYSQL_TYPE_STRING, 100 + 1); // fix b.is_null = 0; b.error = 0; @@ -595,7 +849,8 @@ private struct Driver(Policy) { b.length = value.length; } - void bind(int n, Date d) { + void bind(int n, Date d) + { auto b = bindAlloc(n, MYSQL_TYPE_DATE, MYSQL_TIME.sizeof); b.is_null = 0; b.error = 0; @@ -606,13 +861,18 @@ private struct Driver(Policy) { p.day = d.day; } - Bind* bindAlloc(int n, int mysql_type, int allocSize) { - if (n==0) throw new DatabaseException("zero index"); - auto idx = n-1; - if (idx > inputBind.length) throw new DatabaseException("bind range error"); - if (idx == inputBind.length) inputBind ~= Bind(); + Bind* bindAlloc(int n, int mysql_type, int allocSize) + { + if (n == 0) + throw new DatabaseException("zero index"); + auto idx = n - 1; + if (idx > inputBind.length) + throw new DatabaseException("bind range error"); + if (idx == inputBind.length) + inputBind ~= Bind(); auto b = &inputBind[idx]; - if (allocSize <= b.data.length) return b; // fix + if (allocSize <= b.data.length) + return b; // fix b.mysql_type = mysql_type; b.allocSize = allocSize; b.data = allocator.allocate(b.allocSize); @@ -621,19 +881,21 @@ private struct Driver(Policy) { } - struct Result { - Statement *stmt; + struct Result + { + Statement* stmt; Allocator allocator; uint columns; Array!Describe describe; Array!Bind bind; Array!MYSQL_BIND mysqlBind; - MYSQL_RES *result_metadata; + MYSQL_RES* result_metadata; int status; static const maxData = 256; - this(Statement* stmt_, int rowArraySize_) { + this(Statement* stmt_, int rowArraySize_) + { stmt = stmt_; //allocator = stmt.allocator; @@ -644,20 +906,25 @@ private struct Driver(Policy) { build_bind(); } - ~this() { + ~this() + { //log("~Result"); - foreach(b; bind) allocator.deallocate(b.data); - if (result_metadata) mysql_free_result(result_metadata); + foreach (b; bind) + allocator.deallocate(b.data); + if (result_metadata) + mysql_free_result(result_metadata); } - void build_describe() { - import core.stdc.string: strlen; + void build_describe() + { + import core.stdc.string : strlen; columns = cast(uint) mysql_stmt_field_count(stmt.stmt); describe.reserve(columns); - for(int i = 0; i < columns; ++i) { + for (int i = 0; i < columns; ++i) + { describe ~= Describe(); auto d = &describe.back(); @@ -671,27 +938,61 @@ private struct Driver(Policy) { } } - void build_bind() { - import core.stdc.string: memset; + void build_bind() + { + import core.stdc.string : memset; import core.memory : GC; bind.reserve(columns); - for(int i = 0; i < columns; ++i) { + for (int i = 0; i < columns; ++i) + { auto d = &describe[i]; bind ~= Bind(); auto b = &bind.back(); - // let in ints for now - if (d.field.type == MYSQL_TYPE_LONG) { - b.mysql_type = d.field.type; + switch (d.field.type) + { + case MYSQL_TYPE_TINY: + b.type = ValueType.Char; + break; + case MYSQL_TYPE_SHORT: + b.type = ValueType.Short; + break; + case MYSQL_TYPE_INT24: + b.mysql_type = MYSQL_TYPE_LONG; + goto case; + case MYSQL_TYPE_LONG: b.type = ValueType.Int; - } else if (d.field.type == MYSQL_TYPE_DATE) { - b.mysql_type = d.field.type; + break; + case MYSQL_TYPE_LONGLONG: + b.type = ValueType.Long; + break; + case MYSQL_TYPE_FLOAT: + b.type = ValueType.Float; + break; + case MYSQL_TYPE_DOUBLE: + b.type = ValueType.Double; + break; + + case MYSQL_TYPE_DATE: b.type = ValueType.Date; - } else { + break; + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_TIME2: + b.type = ValueType.Time; + break; + case MYSQL_TYPE_DATETIME2: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP2: + case MYSQL_TYPE_TIMESTAMP: + b.type = ValueType.DateTime; + break; + default: b.mysql_type = MYSQL_TYPE_STRING; b.type = ValueType.String; + break; + } b.allocSize = cast(uint)(d.field.length + 1); @@ -703,14 +1004,20 @@ private struct Driver(Policy) { mysql_stmt_bind_result(stmt.stmt, &mysqlBind.front()); } - int fetch() { + int fetch() + { status = check("mysql_stmt_fetch", stmt.stmt, mysql_stmt_fetch(stmt.stmt)); - if (!status) { + if (!status) + { return 1; - } else if (status == MYSQL_NO_DATA) { + } + else if (status == MYSQL_NO_DATA) + { //rows_ = row_count_; return 0; - } else if (status == MYSQL_DATA_TRUNCATED) { + } + else if (status == MYSQL_DATA_TRUNCATED) + { raiseError("mysql_stmt_fetch: truncation", status); } @@ -718,49 +1025,129 @@ private struct Driver(Policy) { return 0; } + Variant getValue(Cell* cell) + { + Variant value; + switch (cell.bind.type) + { + case ValueType.Char: + value = (*cast(char*) cell.bind.data.ptr); + break; + case ValueType.Short: + value = (*cast(short*) cell.bind.data.ptr); + break; + case ValueType.Int: + value = (*cast(int*) cell.bind.data.ptr); + break; + case ValueType.Long: + value = (*cast(long*) cell.bind.data.ptr); + break; + case ValueType.Float: + value = (*cast(float*) cell.bind.data.ptr); + break; + case ValueType.Double: + value = (*cast(double*) cell.bind.data.ptr); + break; + case ValueType.String: + { + auto ptr = cast(char*) cell.bind.data.ptr; + value = cast(string)(ptr[0 .. cell.bind.length]); + } + break; + case ValueType.Date: + { + MYSQL_TIME* t = cast(MYSQL_TIME*) cell.bind.data.ptr; + value = Date(t.year, t.month, t.day); + } + break; + case ValueType.Time: + { + MYSQL_TIME* t = cast(MYSQL_TIME*) cell.bind.data.ptr; + value = Time(t.hour, t.minute, t.second); + } + break; + case ValueType.DateTime: + { + MYSQL_TIME* t = cast(MYSQL_TIME*) cell.bind.data.ptr; + value = DateTime(t.year, t.month, t.day, t.hour, t.minute, + t.second); + } + break; + default: + break; + } + return value; + } + // value getters - auto name(size_t idx) { + auto name(size_t idx) + { return describe[idx].name; } - char[] get(X:char[])(Bind *b) { - auto ptr = cast(char*) b.data.ptr; - return ptr[0..b.length]; + ubyte[] rawData(Cell* cell) + { + auto ptr = cast(ubyte*) cell.bind.data.ptr; + return ptr[0 .. cell.bind.length]; } - auto get(X:string)(Cell* cell) { - //return cast(string) get!(char[])(b); - auto ptr = cast(char*) cell.bind.data.ptr; - return ptr[0..cell.bind.length]; + bool isNull(Cell* cell) + { + return cell.bind.is_null > 0; } - auto get(X:int)(Cell* cell) { - return *cast(int*) cell.bind.data.ptr; + auto get(X : string)(Cell* cell) + { + return getValue(cell).toString(); } - auto get(X:Date)(Cell* cell) { - //return Date(2016,1,1); // fix - MYSQL_TIME *t = cast(MYSQL_TIME*) cell.bind.data.ptr; - //t.year,t.month,t.day,t.hour,t.minute,t.second - return Date(t.year,t.month,t.day); + auto get(X : int)(Cell* cell) + { + return getValue(cell).get!X(); } - static void checkType(T)(Bind *b) { + auto get(X : Date)(Cell* cell) + { + return getValue(cell).get!X(); + } + + static void checkType(T)(Bind* b) + { int x = TypeInfo!T.type(); int y = b.mysql_type; - if (x == y) return; - warning("type pair mismatch: ",x, ":", y); + if (x == y) + return; + warning("type pair mismatch: ", x, ":", y); throw new DatabaseException("type mismatch"); } // refactor as a better 1-n bind mapping - struct TypeInfo(T:int) {static int type() {return MYSQL_TYPE_LONG;}} - struct TypeInfo(T:string) {static int type() {return MYSQL_TYPE_STRING;}} - struct TypeInfo(T:Date) {static int type() {return MYSQL_TYPE_DATE;}} + struct TypeInfo(T : int) + { + static int type() + { + return MYSQL_TYPE_LONG; + } + } + + struct TypeInfo(T : string) + { + static int type() + { + return MYSQL_TYPE_STRING; + } + } + + struct TypeInfo(T : Date) + { + static int type() + { + return MYSQL_TYPE_DATE; + } + } } } - } diff --git a/src/std/database/odbc/database.d b/src/std/database/odbc/database.d index f6e6dd9..a3608d0 100644 --- a/src/std/database/odbc/database.d +++ b/src/std/database/odbc/database.d @@ -419,6 +419,13 @@ struct Driver(Policy) { return cast(string) d.name[0..d.nameLen]; } + ubyte[] rawData(Cell* cell) { + auto ptr = cast(ubyte*) cell.bind.data; + return ptr[0..cell.bind.len]; + } + + bool isNull(Cell* cell){return false;} + auto get(X:string)(Cell* cell) { checkType(cell.bind.bindType, SQL_C_CHAR); auto ptr = cast(immutable char*) cell.bind.data; diff --git a/src/std/database/oracle/database.d b/src/std/database/oracle/database.d index 98b90ba..89dcd15 100644 --- a/src/std/database/oracle/database.d +++ b/src/std/database/oracle/database.d @@ -621,6 +621,14 @@ struct Driver(Policy) { //return 0; } + ubyte[] rawData(Cell* cell) { //TODO: fix + return null; + //auto ptr = cast(ubyte*) data(cell); + //return ptr[0..strlen(ptr)]; + } + + bool isNull(Cell* cell){return false;} + auto get(X:string)(Cell* cell) { import core.stdc.string: strlen; checkType(cell.bind.oType, SQLT_STR); diff --git a/src/std/database/poly/database.d b/src/std/database/poly/database.d index 4395d39..8627445 100644 --- a/src/std/database/poly/database.d +++ b/src/std/database/poly/database.d @@ -394,6 +394,12 @@ struct Driver(Policy) { return 0; } + ubyte[] rawData(Cell* cell) { + return null; + } + + bool isNull(Cell* cell){return false;} + auto name(size_t idx) { return "-name-"; } diff --git a/src/std/database/postgres/database.d b/src/std/database/postgres/database.d index fb038f3..2c69e30 100644 --- a/src/std/database/postgres/database.d +++ b/src/std/database/postgres/database.d @@ -483,6 +483,14 @@ struct Driver(Policy) { return describe[idx].name; } + ubyte[] rawData(Cell* cell){ + auto ptr = cast(ubyte*) data(cell.bind.idx); + return ptr[0..len(cell.bind.idx)]; + } + + + bool isNull(Cell* cell){return false;} + auto get(X:string)(Cell* cell) { checkType(type(cell.bind.idx),VARCHAROID); immutable char *ptr = cast(immutable char*) data(cell.bind.idx); diff --git a/src/std/database/sqlite/database.d b/src/std/database/sqlite/database.d index 63106a7..c379c71 100644 --- a/src/std/database/sqlite/database.d +++ b/src/std/database/sqlite/database.d @@ -242,6 +242,12 @@ struct Driver(Policy) { return cast(string) ptr[0..strlen(ptr)]; } + ubyte[] rawData(Cell* cell) { + return null; //TODO: fix + } + + bool isNull(Cell* cell){return false;} + auto get(X:string)(Cell* cell) { import core.stdc.string: strlen; auto ptr = cast(immutable char*) sqlite3_column_text(st_, cast(int) cell.bind.idx); From 94485b77af193b6d5ec412d714301997271a8727 Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Mon, 4 Jul 2016 14:29:05 +0800 Subject: [PATCH 03/30] add more type support --- src/std/database/allocator.d | 8 +- src/std/database/freetds/database.d | 42 +++---- src/std/database/front.d | 136 +++++++++-------------- src/std/database/mysql/database.d | 160 ++++++++++++--------------- src/std/database/odbc/database.d | 26 ++--- src/std/database/option.d | 68 ------------ src/std/database/oracle/database.d | 48 +++++--- src/std/database/poly/database.d | 19 +--- src/std/database/postgres/database.d | 57 ++++++---- src/std/database/rowset.d | 85 -------------- src/std/database/sqlite/database.d | 40 ++++--- 11 files changed, 253 insertions(+), 436 deletions(-) delete mode 100644 src/std/database/option.d delete mode 100644 src/std/database/rowset.d diff --git a/src/std/database/allocator.d b/src/std/database/allocator.d index a732e45..e6c4497 100644 --- a/src/std/database/allocator.d +++ b/src/std/database/allocator.d @@ -9,8 +9,8 @@ struct MyMallocator { void[] allocate(size_t bytes) { import core.stdc.stdlib : malloc; if (!bytes) return null; - auto p = malloc(bytes + 1); - log("allocate: ptr: ", p , " size :",bytes); + auto p = malloc(bytes); + //log("allocate: ptr: ", p , " size :",bytes); return p ? p[0 .. bytes] : null; } @@ -18,9 +18,9 @@ struct MyMallocator { @system // removed @nogc and nothrow for logging bool deallocate(void[] b) { import core.stdc.stdlib : free; - log("deallocate: ptr: ", b.ptr, " size: ", b.length); + //log("deallocate: ptr: ", b.ptr, " size: ", b.length); free(b.ptr); - log("return true"); + //log("return true"); return true; } diff --git a/src/std/database/freetds/database.d b/src/std/database/freetds/database.d index 79659a0..2a4e7e8 100644 --- a/src/std/database/freetds/database.d +++ b/src/std/database/freetds/database.d @@ -344,28 +344,28 @@ struct Driver(Policy) { return null; } - bool isNull(Cell* cell){return false;} - - auto get(X:string)(Cell* cell) { - import core.stdc.string: strlen; - checkType(cell.bind.bindType, NTBSTRINGBIND); - auto ptr = cast(immutable char*) cell.bind.data.ptr; - return cast(string) ptr[0..strlen(ptr)]; - } - - auto get(X:int)(Cell* cell) { - //if (b.bindType == SQL_C_CHAR) return to!int(as!string()); // tmp hack - //checkType(b.bindType, SQL_C_LONG); - //return *(cast(int*) b.data); - return 0; - } + Variant getValue(Cell* cell) + { + Variant value; + if(cell.bind.type == ValueType.Date) + { + auto ptr = cast(DBDATETIME*) cell.bind.data.ptr; + DBDATEREC d; + check("dbdatecrack", dbdatecrack(dbproc, &d, ptr)); + value = Date(d.year, d.month, d.day); + + } + else + { + import core.stdc.string: strlen; + checkType(cell.bind.bindType, NTBSTRINGBIND); + auto ptr = cast(immutable char*) cell.bind.data.ptr; + value = cast(string) ptr[0..strlen(ptr)]; + } + return value; + } - auto get(X:Date)(Cell* cell) { - auto ptr = cast(DBDATETIME*) cell.bind.data.ptr; - DBDATEREC d; - check("dbdatecrack", dbdatecrack(dbproc, &d, ptr)); - return Date(d.year, d.month, d.day); - } + bool isNull(Cell* cell){return false;} void checkType(int a, int b) { if (a != b) throw new DatabaseException("type mismatch"); diff --git a/src/std/database/front.d b/src/std/database/front.d index f6de93a..3c3db01 100644 --- a/src/std/database/front.d +++ b/src/std/database/front.d @@ -15,7 +15,6 @@ import std.container.array; public import std.variant; import std.range.primitives; -import std.database.option; public import std.database.array; @@ -544,7 +543,6 @@ struct BasicRow(D,P) { auto result = &rows_.result_; // needs work // sending a ptr to cell instead of reference (dangerous) - //auto cell = Cell(result, &result.data_.bind[idx], idx); return Value(Cell(result, &result.data_.bind[idx], idx)); } } @@ -557,70 +555,42 @@ struct BasicValue(D,P) { alias Result = BasicResult!(Driver,Policy); alias Cell = BasicCell!(Driver,Policy); alias Bind = Driver.Bind; - // private Result* result_; - // private Bind* bind_; private Cell cell_; private Variant data_; alias Converter = .Converter!(Driver,Policy); - this(/*Result* result,*/ Cell cell) { - // result_ = result; - //bind_ = bind; + this(Cell cell) { cell_ = cell; + data_ = resultPtr.getValue(&cell_); } private auto resultPtr() { auto r = cell_.result_; return &(r.result()); // last parens matter here (something about delegate) } + + Variant value(){return data_;} + ubyte[] rawData(){return resultPtr.rawData(&cell_);} auto type() {return cell_.bind.type;} - auto as(T:int)() {return Converter.convert!T(resultPtr, cell_);} - auto as(T:string)() {return Converter.convert!T(resultPtr, cell_);} - auto as(T:Date)() {return Converter.convert!T(resultPtr, cell_);} - auto as(T:Nullable!T)() {return Nullable!T(as!T);} + auto as(T)(){return data_.coerce!T();} - // should be nothrow? - auto as(T:Variant)() {return Converter.convert!T(resultPtr, cell_);} + auto as(T:Variant)() {return data_;}//Converter.convert!T(resultPtr, cell_);} bool isNull() {return resultPtr.isNull(&cell_);} //fix string name() {return resultPtr.name(cell_.idx_);} - auto get(T)() {return Nullable!T(as!T);} - - // experimental - auto option(T)() {return Option!T(as!T);} + auto get(T)() {if(data_.convertsTo!T()) return Nullable!T(as!T); else return Nullable!T;} // not sure if this does anything - const(char)[] chars() {return as!string;} - - string toString() {return as!string;} - -} - - - -// extra stuff - -/* -struct EfficientValue(T) { - alias Driver = T.Driver; - alias Bind = Driver.Bind; - private Bind* bind_; - alias Converter = .Converter!Driver; + //const(char)[] chars() {return as!string;} - this(Bind* bind) {bind_ = bind;} - - auto as(T:int)() {return Converter.convertDirect!T(bind_);} - auto as(T:string)() {return Converter.convertDirect!T(bind_);} - auto as(T:Date)() {return Converter.convertDirect!T(bind_);} - auto as(T:Variant)() {return Converter.convertDirect!T(bind_);} + string toString() {return data_.toString();} } -*/ struct BasicCell(D,P) { alias Driver = D; @@ -650,39 +620,40 @@ struct BasicCell(D,P) { auto rowIdx() {return rowIdx_;} } +//Converter now is not used, but if del it ,it will build error. struct Converter(D,P) { alias Driver = D; alias Policy = P; alias Result = Driver.Result; - alias Bind = Driver.Bind; +// alias Bind = Driver.Bind; alias Cell = BasicCell!(Driver,Policy); - static Y convert(Y)(Result *r, ref Cell cell) { - ValueType x = cell.bind.type, y = TypeInfo!Y.type; + // static Y convert(Y)(Result *r, ref Cell cell) { + /* ValueType x = cell.bind.type, y = TypeInfo!Y.type; if (x == y) return r.get!Y(&cell); // temporary auto e = lookup(x,y); if (!e) conversionError(x,y); Y value; - e.convert(r, &cell, &value); - return value; - } + e.convert(r, &cell, &value);*/ +// return Y.init; +// } - static Y convert(Y:Variant)(Result *r, ref Cell cell) { + // static Y convert(Y:Variant)(Result *r, ref Cell cell) { //return Y(123); - ValueType x = cell.bind.type, y = ValueType.Variant; + /* ValueType x = cell.bind.type, y = ValueType.Variant; auto e = lookup(x,y); if (!e) conversionError(x,y); Y value; - e.convert(r, &cell, &value); - return value; - } + e.convert(r, &cell, &value);*/ +// return Variant(); + // } - static Y convertDirect(Y)(Result *r, ref Cell cell) { - assert(b.type == TypeInfo!Y.type); - return r.get!Y(&cell); - } + // static Y convertDirect(Y)(Result *r, ref Cell cell) { + // assert(b.type == TypeInfo!Y.type); +// return Y.init;//return r.get!Y(&cell); + // } - private: + // private: struct Elem { ValueType from,to; @@ -690,45 +661,44 @@ struct Converter(D,P) { } // only cross converters, todo: all converters - static Elem[6] converters = [ - {from: ValueType.Int, to: ValueType.String, &generate!(int,string).convert}, - {from: ValueType.String, to: ValueType.Int, &generate!(string,int).convert}, - {from: ValueType.Date, to: ValueType.String, &generate!(Date,string).convert}, + static Elem[1] converters = [ + // {from: ValueType.Int, to: ValueType.String, &generate!(int,string).convert}, + // {from: ValueType.String, to: ValueType.Int, &generate!(string,int).convert}, + //{from: ValueType.Date, to: ValueType.String, &generate!(Date,string).convert}, // variants {from: ValueType.Int, to: ValueType.Variant, &generate!(int,Variant).convert}, - {from: ValueType.String, to: ValueType.Variant, &generate!(string,Variant).convert}, - {from: ValueType.Date, to: ValueType.Variant, &generate!(Date,Variant).convert}, + //{from: ValueType.String, to: ValueType.Variant, &generate!(string,Variant).convert}, + //{from: ValueType.Date, to: ValueType.Variant, &generate!(Date,Variant).convert}, ]; - static Elem* lookup(ValueType x, ValueType y) { + // static Elem* lookup(ValueType x, ValueType y) { // rework into efficient array lookup - foreach(ref i; converters) { - if (i.from == x && i.to == y) return &i; - } - return null; - } + // foreach(ref i; converters) { + // if (i.from == x && i.to == y) return &i; + // } + // return null; + // } struct generate(X,Y) { static void convert(Result *r, void *x_, void *y_) { - import std.conv; + // import std.conv; Cell* cell = cast(Cell*) x_; - *cast(Y*) y_ = to!Y(r.get!X(cell)); + // *cast(Y*) y_ = to!Y(r.get!X(cell)); } } - struct generate(X,Y:Variant) { - static void convert(Result *r, void *x_, void *y_) { - Cell* cell = cast(Cell*) x_; - *cast(Y*) y_ = r.get!X(cell); - } - } - - static void conversionError(ValueType x, ValueType y) { - import std.conv; - string msg; - msg ~= "unsupported conversion from: " ~ to!string(x) ~ " to " ~ to!string(y); - throw new DatabaseException(msg); - } + // struct generate(X,Y:Variant) { + // static void convert(Result *r, void *x_, void *y_) { + // Cell* cell = cast(Cell*) x_; + // *cast(Y*) y_ = r.get!X(cell); + // } + // } + // static void conversionError(ValueType x, ValueType y) { + // import std.conv; + // string msg; + // msg ~= "unsupported conversion from: " ~ to!string(x) ~ " to " ~ to!string(y); + // throw new DatabaseException(msg); + // } } diff --git a/src/std/database/mysql/database.d b/src/std/database/mysql/database.d index 902d72f..90fc31d 100644 --- a/src/std/database/mysql/database.d +++ b/src/std/database/mysql/database.d @@ -179,10 +179,10 @@ private struct Driver(Policy) for (int i = 0; i != bind.length; ++i) { mysqlBind ~= MYSQL_BIND(); - //import core.stdc.string: memset; - //memset(mb, 0, MYSQL_BIND.sizeof); // might not be needed: D struct auto b = &bind[i]; auto mb = &mysqlBind[i]; + //import core.stdc.string: memset; + //memset(mb, 0, MYSQL_BIND.sizeof); // might not be needed: D struct mb.buffer_type = b.mysql_type; mb.buffer = b.data.ptr; mb.buffer_length = b.allocSize; @@ -409,6 +409,7 @@ private struct Driver(Policy) bind ~= Bind(); auto b = &bind.back(); b.mysql_type = d.field.type; + b.allocSize = cast(uint)(d.field.length + 1); switch (d.field.type) { case MYSQL_TYPE_TINY: @@ -417,10 +418,6 @@ private struct Driver(Policy) case MYSQL_TYPE_SHORT: b.type = ValueType.Short; break; - /* case MYSQL_TYPE_INT24: - b.mysql_type = MYSQL_TYPE_STRING; - b.type = ValueType. - goto case;*/ case MYSQL_TYPE_LONG: b.type = ValueType.Int; break; @@ -440,16 +437,19 @@ private struct Driver(Policy) case MYSQL_TYPE_TIME: case MYSQL_TYPE_TIME2: b.type = ValueType.Time; + b.allocSize += 30; break; case MYSQL_TYPE_DATETIME2: case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_TIMESTAMP2: case MYSQL_TYPE_TIMESTAMP: b.type = ValueType.DateTime; + b.allocSize += 30; break; default: b.mysql_type = MYSQL_TYPE_STRING; b.type = ValueType.String; + b.allocSize += 256; break; } @@ -464,8 +464,6 @@ private struct Driver(Policy) b.mysql_type = MYSQL_TYPE_STRING; b.type = ValueType.String; }*/ - - b.allocSize = cast(uint)(d.field.length + 1); b.data = allocator.allocate(b.allocSize); } @@ -583,8 +581,9 @@ private struct Driver(Policy) */ ubyte[] rawData(Cell* cell) { + log("-----------bind.bufferLength = ", cell.bind.allocSize , " , dataLength = ", cell.bind.length); auto ptr = cast(ubyte*) cell.bind.data.ptr; - return ptr[0 .. cell.bind.length]; + return ptr[0 .. cell.bind.length]; } bool isNull(Cell* cell) @@ -597,22 +596,6 @@ private struct Driver(Policy) return describe[idx].name; } - auto get(X : string)(Cell* cell) - { - writeln("tostring``````````"); - return getValue(cell).toString(); - } - - auto get(X : int)(Cell* cell) - { - return getValue(cell).get!X(); - } - - auto get(X : Date)(Cell* cell) - { - return getValue(cell).get!X(); - } - static void checkType(T)(Bind* b) { int x = TypeInfo!T.type(); @@ -945,61 +928,71 @@ private struct Driver(Policy) bind.reserve(columns); - for (int i = 0; i < columns; ++i) - { - auto d = &describe[i]; - bind ~= Bind(); - auto b = &bind.back(); - - switch (d.field.type) - { - case MYSQL_TYPE_TINY: - b.type = ValueType.Char; - break; - case MYSQL_TYPE_SHORT: - b.type = ValueType.Short; - break; - case MYSQL_TYPE_INT24: - b.mysql_type = MYSQL_TYPE_LONG; - goto case; - case MYSQL_TYPE_LONG: + for (int i = 0; i != columns; ++i) + { + auto d = &describe[i]; + bind ~= Bind(); + auto b = &bind.back(); + b.mysql_type = d.field.type; + b.allocSize = cast(uint)(d.field.length + 1); + switch (d.field.type) + { + case MYSQL_TYPE_TINY: + b.type = ValueType.Char; + break; + case MYSQL_TYPE_SHORT: + b.type = ValueType.Short; + break; + case MYSQL_TYPE_LONG: + b.type = ValueType.Int; + break; + case MYSQL_TYPE_LONGLONG: + b.type = ValueType.Long; + break; + case MYSQL_TYPE_FLOAT: + b.type = ValueType.Float; + break; + case MYSQL_TYPE_DOUBLE: + b.type = ValueType.Double; + break; + + case MYSQL_TYPE_DATE: + b.type = ValueType.Date; + break; + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_TIME2: + b.type = ValueType.Time; + b.allocSize += 30; + break; + case MYSQL_TYPE_DATETIME2: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP2: + case MYSQL_TYPE_TIMESTAMP: + b.type = ValueType.DateTime; + b.allocSize += 30; + break; + default: + b.mysql_type = MYSQL_TYPE_STRING; + b.type = ValueType.String; + b.allocSize += 256; + break; + + } + // let in ints for now + /* if (d.field.type == MYSQL_TYPE_LONG) { + b.mysql_type = d.field.type; b.type = ValueType.Int; - break; - case MYSQL_TYPE_LONGLONG: - b.type = ValueType.Long; - break; - case MYSQL_TYPE_FLOAT: - b.type = ValueType.Float; - break; - case MYSQL_TYPE_DOUBLE: - b.type = ValueType.Double; - break; - - case MYSQL_TYPE_DATE: + } else if (d.field.type == MYSQL_TYPE_DATE) { + b.mysql_type = d.field.type; b.type = ValueType.Date; - break; - case MYSQL_TYPE_TIME: - case MYSQL_TYPE_TIME2: - b.type = ValueType.Time; - break; - case MYSQL_TYPE_DATETIME2: - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIMESTAMP2: - case MYSQL_TYPE_TIMESTAMP: - b.type = ValueType.DateTime; - break; - default: + } else { b.mysql_type = MYSQL_TYPE_STRING; b.type = ValueType.String; - break; - - } - - b.allocSize = cast(uint)(d.field.length + 1); - b.data = allocator.allocate(b.allocSize); - } - - bindSetup(bind, mysqlBind); + }*/ + b.data = allocator.allocate(b.allocSize); + } + + bindSetup(bind, mysqlBind); mysql_stmt_bind_result(stmt.stmt, &mysqlBind.front()); } @@ -1097,21 +1090,6 @@ private struct Driver(Policy) return cell.bind.is_null > 0; } - auto get(X : string)(Cell* cell) - { - return getValue(cell).toString(); - } - - auto get(X : int)(Cell* cell) - { - return getValue(cell).get!X(); - } - - auto get(X : Date)(Cell* cell) - { - return getValue(cell).get!X(); - } - static void checkType(T)(Bind* b) { int x = TypeInfo!T.type(); diff --git a/src/std/database/odbc/database.d b/src/std/database/odbc/database.d index a3608d0..35ccca9 100644 --- a/src/std/database/odbc/database.d +++ b/src/std/database/odbc/database.d @@ -424,23 +424,19 @@ struct Driver(Policy) { return ptr[0..cell.bind.len]; } - bool isNull(Cell* cell){return false;} + Variant getValue(Cell* cell) + { + Variant = value; + if(cell.bind.type = ValueType.String) + { + auto ptr = cast(immutable char*) cell.bind.data; + value = cast(string) ptr[0..cell.bind.len]; + } + return value; //TODO: + } - auto get(X:string)(Cell* cell) { - checkType(cell.bind.bindType, SQL_C_CHAR); - auto ptr = cast(immutable char*) cell.bind.data; - return cast(string) ptr[0..cell.bind.len]; - } - auto get(X:int)(Cell* cell) { - //if (b.bindType == SQL_C_CHAR) return to!int(as!string()); // tmp hack - checkType(cell.bind.bindType, SQL_C_LONG); - return *(cast(int*) cell.bind.data); - } - - auto get(X:Date)(Cell* cell) { - return Date(2016,1,1); // fix - } + bool isNull(Cell* cell){return false;} void checkType(SQLSMALLINT a, SQLSMALLINT b) { if (a != b) throw new DatabaseException("type mismatch"); diff --git a/src/std/database/option.d b/src/std/database/option.d deleted file mode 100644 index 922e9e8..0000000 --- a/src/std/database/option.d +++ /dev/null @@ -1,68 +0,0 @@ -module std.database.option; - -struct Option(T) { - private T _value; - private bool _isNull = true; - - this(inout T value) inout { - _value = value; - _isNull = false; - } - - template toString() { - import std.format : FormatSpec, formatValue; - // Needs to be a template because of DMD @@BUG@@ 13737. - void toString()(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) { - if (isNull) - { - sink.formatValue("Nullable.null", fmt); - } - else - { - sink.formatValue(_value, fmt); - } - } - - // Issue 14940 - void toString()(scope void delegate(const(char)[]) @safe sink, FormatSpec!char fmt) { - if (isNull) - { - sink.formatValue("Nullable.null", fmt); - } - else - { - sink.formatValue(_value, fmt); - } - } - } - - @property bool isNull() const @safe pure nothrow { - return _isNull; - } - - void nullify()() { - .destroy(_value); - _isNull = true; - } - - void opAssign()(T value) { - _value = value; - _isNull = false; - } - - @property ref inout(T) get() inout @safe pure nothrow { - enum message = "Called `get' on null Nullable!" ~ T.stringof ~ "."; - assert(!isNull, message); - return _value; - } - - @property ref inout(T) front() inout @safe pure nothrow {return get;} - @property bool empty() const @safe pure nothrow {return isNull;} - @property void popFront() @safe pure nothrow {nullify;} - - //Implicitly converts to T: must not be in the null state. - alias get this; - - -} - diff --git a/src/std/database/oracle/database.d b/src/std/database/oracle/database.d index 89dcd15..a30f81a 100644 --- a/src/std/database/oracle/database.d +++ b/src/std/database/oracle/database.d @@ -627,31 +627,43 @@ struct Driver(Policy) { //return ptr[0..strlen(ptr)]; } + Variant getValue(Cell* cell) + { + Variant value; + switch(cell.bind.oType) + { + case SQLT_STR: + { + import core.stdc.string: strlen; + //checkType(cell.bind.oType, SQLT_STR); + auto ptr = cast(immutable char*) data(cell); + value = cast(string) ptr[0..strlen(ptr)]; // fix with length + } + break; + case SQLT_INT: + value = *(cast(int*) data(cell)); + break; + case SQLT_ODT: + { + auto d = cast(OCIDate*) data(cell); + value = Date(d.OCIDateYYYY,d.OCIDateMM,d.OCIDateDD); + } + break; + default: + break; + + } + return value; //TODO: + } + + bool isNull(Cell* cell){return false;} - auto get(X:string)(Cell* cell) { - import core.stdc.string: strlen; - checkType(cell.bind.oType, SQLT_STR); - auto ptr = cast(immutable char*) data(cell); - return cast(string) ptr[0..strlen(ptr)]; // fix with length - } auto name(size_t idx) { return describe[idx].name; } - auto get(X:int)(Cell* cell) { - checkType(cell.bind.oType, SQLT_INT); - return *(cast(int*) data(cell)); - } - - auto get(X:Date)(Cell* cell) { - //return Date(2016,1,1); // fix - checkType(cell.bind.oType, SQLT_ODT); - auto d = cast(OCIDate*) data(cell); - return Date(d.OCIDateYYYY,d.OCIDateMM,d.OCIDateDD); - } - private void* data(Cell* cell) { return cell.bind.data.ptr + cell.bind.allocSize * cell.rowIdx; } diff --git a/src/std/database/poly/database.d b/src/std/database/poly/database.d index 8627445..6b8b1ac 100644 --- a/src/std/database/poly/database.d +++ b/src/std/database/poly/database.d @@ -398,24 +398,17 @@ struct Driver(Policy) { return null; } + Variant getValue(Cell* cell) + { + return Variant; //TODO: + } + + bool isNull(Cell* cell){return false;} auto name(size_t idx) { return "-name-"; } - - auto get(X:string)(Cell* cell) { - return "abc"; - } - - auto get(X:int)(Cell* cell) { - return 0; - } - - auto get(X:Date)(Cell* cell) { - return Date(2016,1,1); // fix - } - } } diff --git a/src/std/database/postgres/database.d b/src/std/database/postgres/database.d index 2c69e30..4a4aa38 100644 --- a/src/std/database/postgres/database.d +++ b/src/std/database/postgres/database.d @@ -488,32 +488,41 @@ struct Driver(Policy) { return ptr[0..len(cell.bind.idx)]; } + Variant getValue(Cell* cell) + { + Variant value; + switch(type(cell.bind.idx)) + { + case VARCHAROID: + { + immutable char *ptr = cast(immutable char*) data(cell.bind.idx); + value = cast(string) ptr[0..len(cell.bind.idx)]; + } + break; + case INT4OID: + { + import std.bitmanip; + auto p = cast(ubyte*) data(cell.bind.idx); + value = bigEndianToNative!int(p[0..int.sizeof]); + } + break; + case DATEOID: + { + import std.bitmanip; + auto ptr = cast(ubyte*) data(cell.bind.idx); + int sz = len(cell.bind.idx); + date d = bigEndianToNative!uint(ptr[0..4]); // why not sz? + int[3] mdy; + PGTYPESdate_julmdy(d, &mdy[0]); + value = Date(mdy[2],mdy[0],mdy[1]); + } + break; + } + return value; //TODO: + } - bool isNull(Cell* cell){return false;} - - auto get(X:string)(Cell* cell) { - checkType(type(cell.bind.idx),VARCHAROID); - immutable char *ptr = cast(immutable char*) data(cell.bind.idx); - return cast(string) ptr[0..len(cell.bind.idx)]; - } - auto get(X:int)(Cell* cell) { - import std.bitmanip; - checkType(type(cell.bind.idx),INT4OID); - auto p = cast(ubyte*) data(cell.bind.idx); - return bigEndianToNative!int(p[0..int.sizeof]); - } - - auto get(X:Date)(Cell* cell) { - import std.bitmanip; - checkType(type(cell.bind.idx),DATEOID); - auto ptr = cast(ubyte*) data(cell.bind.idx); - int sz = len(cell.bind.idx); - date d = bigEndianToNative!uint(ptr[0..4]); // why not sz? - int[3] mdy; - PGTYPESdate_julmdy(d, &mdy[0]); - return Date(mdy[2],mdy[0],mdy[1]); - } + bool isNull(Cell* cell){return false;} void checkType(int a, int b) { if (a != b) throw new DatabaseException("type mismatch"); diff --git a/src/std/database/rowset.d b/src/std/database/rowset.d deleted file mode 100644 index b376302..0000000 --- a/src/std/database/rowset.d +++ /dev/null @@ -1,85 +0,0 @@ -module std.database.rowset; -import std.database.front; -import std.datetime; -import std.container.array; - -// experimental detached rowset - -/* -struct RowSet { - private struct RowData { - int[3] data; - } - private alias Data = Array!RowData; - - struct Row { - private RowSet* rowSet; - private RowData* data; - this(RowSet* rs, RowData *d) {rowSet = rs; data = d;} - int columns() {return rowSet.columns();} - auto opIndex(size_t idx) {return Value(rowSet,Bind(ValueType.Int, &data.data[idx]));} - } - - struct Bind { - this(ValueType t, void* d) {type = t; data = d;} - ValueType type; - void *data; - } - - struct Driver { - alias Result = .RowSet; - alias Bind = RowSet.Bind; - } - struct Policy {} - - alias Converter = .Converter!(Driver,Policy); - - - struct TypeInfo(T:int) {static int type() {return ValueType.Int;}} - struct TypeInfo(T:string) {static int type() {return ValueType.String;}} - struct TypeInfo(T:Date) {static int type() {return ValueType.Date;}} - - static auto get(X:string)(Bind *b) {return "";} - static auto get(X:int)(Bind *b) {return *cast(int*) b;} - static auto get(X:Date)(Bind *b) {return Date(2016,1,1);} - - struct Value { - RowSet* rowSet; - Bind bind; - private void* data; - this(RowSet *r, Bind b) { - rowSet = r; - bind = b; - } - //auto as(T:int)() {return *cast(int*) bind.data;} - auto as(T:int)() {return Converter.convert!T(rowSet,&bind);} - } - - struct Range { - alias Rng = Data.Range; - private RowSet* rowSet; - private Rng rng; - this(ref RowSet rs, Rng r) {rowSet = &rs; rng = r;} - bool empty() {return rng.empty();} - Row front() {return Row(rowSet,&rng.front());} - void popFront() {rng.popFront();} - } - - - this(R) (R result, size_t capacity = 0) { - data.reserve(capacity); - foreach (r; result[]) { - data ~= RowData(); - auto d = &data.back(); - for(int c = 0; c != r.columns; ++c) d.data[c] = r[c].as!int; - } - } - - int columns() {return cast(int) data.length;} - auto opSlice() {return Range(this,data[]);} - - private Array!RowData data; -} - -*/ - diff --git a/src/std/database/sqlite/database.d b/src/std/database/sqlite/database.d index c379c71..5c4baf7 100644 --- a/src/std/database/sqlite/database.d +++ b/src/std/database/sqlite/database.d @@ -246,22 +246,34 @@ struct Driver(Policy) { return null; //TODO: fix } - bool isNull(Cell* cell){return false;} - - auto get(X:string)(Cell* cell) { - import core.stdc.string: strlen; - auto ptr = cast(immutable char*) sqlite3_column_text(st_, cast(int) cell.bind.idx); - return cast(string) ptr[0..strlen(ptr)]; // fix with length - } - - auto get(X:int)(Cell* cell) { - return sqlite3_column_int(st_, cast(int) cell.bind.idx); - } + Variant getValue(Cell* cell){ + Variant value; + int idx = cast(int)cell.bind.idx; + switch(sqlite3_column_type(st_,idx)) + { + case SQLITE_INTEGER: + value = sqlite3_column_int(st_, idx); + break; + case SQLITE_FLOAT: + value = sqlite3_column_double(st_,idx); + break; + case SQLITE3_TEXT: + { + import core.stdc.string: strlen; + auto ptr = cast(immutable char*) sqlite3_column_text(st_, cast(int) cell.bind.idx); + value = cast(string) ptr[0..strlen(ptr)]; // fix with length + } + break; + case SQLITE_BLOB: + break; + case SQLITE_NULL: + break; + } + return value; //TODO: + } - auto get(X:Date)(Cell* cell) { - return Date(2016,1,1); // fix - } + bool isNull(Cell* cell){return sqlite3_column_type(st_,cast(int)cell.bind.idx) == SQLITE_NULL;} } private static void throw_error()(sqlite3 *sq, string msg, int ret) { From 7fc4dc428f4f8d7001f2522216c2c05c25acf863 Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Mon, 4 Jul 2016 14:33:29 +0800 Subject: [PATCH 04/30] up gitignore --- .directory | 6 -- .gitignore | 4 ++ .project | 18 ----- dub.json.user | 186 -------------------------------------------------- dub.userprefs | 8 --- 5 files changed, 4 insertions(+), 218 deletions(-) delete mode 100644 .directory delete mode 100644 .project delete mode 100644 dub.json.user delete mode 100644 dub.userprefs diff --git a/.directory b/.directory deleted file mode 100644 index 10eae53..0000000 --- a/.directory +++ /dev/null @@ -1,6 +0,0 @@ -[Dolphin] -Timestamp=2016,6,3,17,52,40 -Version=3 - -[Settings] -HiddenFilesShown=true diff --git a/.gitignore b/.gitignore index 70d1fa2..ebe4f14 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,8 @@ dub.selections.json #unit test binaries tests/*/tests __test__*__ +*.user +*.userprefs +.directory +.project diff --git a/.project b/.project deleted file mode 100644 index cf2333b..0000000 --- a/.project +++ /dev/null @@ -1,18 +0,0 @@ - - - dstddb - - - - - - org.dsource.ddt.ide.core.DubBuilder - clean,full,incremental, - - - - - - org.dsource.ddt.ide.core.nature - - diff --git a/dub.json.user b/dub.json.user deleted file mode 100644 index 6772844..0000000 --- a/dub.json.user +++ /dev/null @@ -1,186 +0,0 @@ - - - - - - DubProjectManager.DubProject.SourceTreeConfiguration - <no config> - - - EnvironmentId - {afc4a408-be65-4fd8-b2d3-bf11fa3941f2} - - - ProjectExplorer.Project.ActiveTarget - 0 - - - ProjectExplorer.Project.EditorSettings - - true - false - true - - Cpp - - CppGlobal - - - - QmlJS - - QmlJSGlobal - - - 2 - UTF-8 - false - 4 - false - 80 - true - true - 1 - true - false - 0 - true - 0 - 8 - true - 1 - true - true - true - false - - - - ProjectExplorer.Project.PluginSettings - - - - 1 - - - - - ProjectExplorer.Project.Target.0 - - Desktop - Desktop - {b3466256-b4e1-4e5f-afc6-26e0639d4f75} - 0 - 0 - 0 - - /home/dsby/code/dlang/github/dstddb - - - - debug - <no config> - - true - - Dub - DubProjectManager.BuildStep - - 1 - 构建 - - ProjectExplorer.BuildSteps.Build - - - 0 - 清理 - - ProjectExplorer.BuildSteps.Clean - - 2 - false - - all - all - DubProjectManager.DubBuildConfiguration - - 1 - - - 0 - 部署 - - ProjectExplorer.BuildSteps.Deploy - - 1 - 在本地部署 - - ProjectExplorer.DefaultDeployConfiguration - - 1 - - - - false - false - false - false - true - 0.01 - 10 - true - 1 - 25 - - 1 - true - false - true - valgrind - - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - - - <no config> - 2 - - dstddb - dstddb - DubProjectManager.RunConfigurationdstddb - 3768 - false - true - false - false - true - - 1 - - - - ProjectExplorer.Project.TargetCount - 1 - - - ProjectExplorer.Project.Updater.FileVersion - 18 - - - Version - 18 - - diff --git a/dub.userprefs b/dub.userprefs deleted file mode 100644 index 0f08895..0000000 --- a/dub.userprefs +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file From eca9f913f070837bb772f0afc6fb08c5c96467ef Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Mon, 4 Jul 2016 14:35:23 +0800 Subject: [PATCH 05/30] rm kdev4 project file --- .gitignore | 4 +++- CMakeLists.txt | 3 --- dstddb.cmake | 44 -------------------------------------------- dstddb.kdev4 | 3 --- 4 files changed, 3 insertions(+), 51 deletions(-) delete mode 100644 CMakeLists.txt delete mode 100644 dstddb.cmake delete mode 100644 dstddb.kdev4 diff --git a/.gitignore b/.gitignore index ebe4f14..d14d3cd 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,6 @@ __test__*__ *.userprefs .directory .project - +*.txt +*.cmake +*.kdev4 diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 0f8236a..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -cmake_minimum_required(VERSION 3.0) -project(dstddb D) -include(/home/dsby/code/dlang/github/dstddb/dstddb.cmake) diff --git a/dstddb.cmake b/dstddb.cmake deleted file mode 100644 index 76e7d7e..0000000 --- a/dstddb.cmake +++ /dev/null @@ -1,44 +0,0 @@ -include(UseD) -add_d_conditions(VERSION Have_dstddb DEBUG ) -include_directories(/home/dsby/code/dlang/github/dstddb/src/) -add_library(dstddb - /home/dsby/code/dlang/github/dstddb/src/std/database/allocator.d - /home/dsby/code/dlang/github/dstddb/src/std/database/array.d - /home/dsby/code/dlang/github/dstddb/src/std/database/common.d - /home/dsby/code/dlang/github/dstddb/src/std/database/exception.d - /home/dsby/code/dlang/github/dstddb/src/std/database/freetds/bindings.d - /home/dsby/code/dlang/github/dstddb/src/std/database/freetds/database.d - /home/dsby/code/dlang/github/dstddb/src/std/database/freetds/package.d - /home/dsby/code/dlang/github/dstddb/src/std/database/front.d - /home/dsby/code/dlang/github/dstddb/src/std/database/mysql/bindings.d - /home/dsby/code/dlang/github/dstddb/src/std/database/mysql/database.d - /home/dsby/code/dlang/github/dstddb/src/std/database/mysql/package.d - /home/dsby/code/dlang/github/dstddb/src/std/database/odbc/database.d - /home/dsby/code/dlang/github/dstddb/src/std/database/odbc/package.d - /home/dsby/code/dlang/github/dstddb/src/std/database/option.d - /home/dsby/code/dlang/github/dstddb/src/std/database/oracle/bindings.d - /home/dsby/code/dlang/github/dstddb/src/std/database/oracle/database.d - /home/dsby/code/dlang/github/dstddb/src/std/database/oracle/package.d - /home/dsby/code/dlang/github/dstddb/src/std/database/oracle/stubs.d - /home/dsby/code/dlang/github/dstddb/src/std/database/package.d - /home/dsby/code/dlang/github/dstddb/src/std/database/poly/database.d - /home/dsby/code/dlang/github/dstddb/src/std/database/poly/package.d - /home/dsby/code/dlang/github/dstddb/src/std/database/pool.d - /home/dsby/code/dlang/github/dstddb/src/std/database/postgres/bindings.d - /home/dsby/code/dlang/github/dstddb/src/std/database/postgres/database.d - /home/dsby/code/dlang/github/dstddb/src/std/database/postgres/package.d - /home/dsby/code/dlang/github/dstddb/src/std/database/reference/database.d - /home/dsby/code/dlang/github/dstddb/src/std/database/reference/package.d - /home/dsby/code/dlang/github/dstddb/src/std/database/resolver.d - /home/dsby/code/dlang/github/dstddb/src/std/database/rowset.d - /home/dsby/code/dlang/github/dstddb/src/std/database/source.d - /home/dsby/code/dlang/github/dstddb/src/std/database/sqlite/database.d - /home/dsby/code/dlang/github/dstddb/src/std/database/sqlite/package.d - /home/dsby/code/dlang/github/dstddb/src/std/database/testsuite.d - /home/dsby/code/dlang/github/dstddb/src/std/database/uri.d - /home/dsby/code/dlang/github/dstddb/src/std/database/util.d - /home/dsby/code/dlang/github/dstddb/src/std/database/variant.d - /home/dsby/code/dlang/github/dstddb/src/std/database/vibehandler.d -) -target_link_libraries(dstddb ) -set_target_properties(dstddb PROPERTIES TEXT_INCLUDE_DIRECTORIES "") diff --git a/dstddb.kdev4 b/dstddb.kdev4 deleted file mode 100644 index f5fe523..0000000 --- a/dstddb.kdev4 +++ /dev/null @@ -1,3 +0,0 @@ -[Project] -Manager=KDevCMakeManager -Name=dstddb From 9a218201fbd4c4edd4ef289973684bd268e6c6ec Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Mon, 4 Jul 2016 14:37:12 +0800 Subject: [PATCH 06/30] rm .kdev4 --- .gitignore | 1 + .kdev4/dstddb.kdev4 | 23 ----------------------- 2 files changed, 1 insertion(+), 23 deletions(-) delete mode 100644 .kdev4/dstddb.kdev4 diff --git a/.gitignore b/.gitignore index d14d3cd..cbbfccf 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ __test__*__ *.txt *.cmake *.kdev4 +.kdev4 diff --git a/.kdev4/dstddb.kdev4 b/.kdev4/dstddb.kdev4 deleted file mode 100644 index 7c942bc..0000000 --- a/.kdev4/dstddb.kdev4 +++ /dev/null @@ -1,23 +0,0 @@ -[Buildset] -BuildItems=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x01\x00\x00\x00\x0b\x00\x00\x00\x00\x01\x00\x00\x00\x0c\x00d\x00s\x00t\x00d\x00d\x00b) - -[CMake] -Build Directory Count=1 -Current Build Directory Index=0 -ProjectRootRelative=./ - -[CMake][CMake Build Directory 0] -Build Directory Path=file:///home/dsby/code/dlang/github/dstddb/build -Build Type=Debug -CMake Binary=file:///usr/bin/cmake -Environment Profile= -Extra Arguments= -Install Directory= - -[Defines And Includes][Compiler] -Name=GCC -Path=gcc -Type=GCC - -[Project] -VersionControlSupport=kdevgit From da4ef682bb6acd68f8771f19a296ca8c85934734 Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Mon, 4 Jul 2016 14:42:11 +0800 Subject: [PATCH 07/30] fmt style --- src/std/database/freetds/database.d | 243 +++---- src/std/database/front.d | 579 +++++++++------ src/std/database/mysql/database.d | 514 +++++--------- src/std/database/odbc/database.d | 258 +++---- src/std/database/oracle/database.d | 988 ++++++++++++-------------- src/std/database/poly/database.d | 443 ++++++------ src/std/database/postgres/database.d | 324 +++++---- src/std/database/reference/database.d | 164 +++-- src/std/database/sqlite/database.d | 181 ++--- 9 files changed, 1857 insertions(+), 1837 deletions(-) diff --git a/src/std/database/freetds/database.d b/src/std/database/freetds/database.d index 2a4e7e8..7736c4c 100644 --- a/src/std/database/freetds/database.d +++ b/src/std/database/freetds/database.d @@ -22,45 +22,41 @@ struct DefaultPolicy { alias Allocator = MyMallocator; } -alias Database(T) = BasicDatabase!(Driver!T,T); +alias Database(T) = BasicDatabase!(Driver!T, T); -auto createDatabase()(string defaultURI="") { - return Database!DefaultPolicy(defaultURI); +auto createDatabase()(string defaultURI = "") { + return Database!DefaultPolicy(defaultURI); } - struct Driver(Policy) { alias Allocator = Policy.Allocator; - alias Cell = BasicCell!(Driver!Policy,Policy); + alias Cell = BasicCell!(Driver!Policy, Policy); private static bool isError(RETCODE ret) { - return - ret != SUCCEED && - ret != REG_ROW && - ret != NO_MORE_ROWS && - ret != NO_MORE_RESULTS; + return ret != SUCCEED && ret != REG_ROW && ret != NO_MORE_ROWS && ret != NO_MORE_RESULTS; } static T* check(T)(string msg, T* object) { info(msg); - if (object == null) throw new DatabaseException("error: " ~ msg); + if (object == null) + throw new DatabaseException("error: " ~ msg); return object; } static RETCODE check(string msg, RETCODE ret) { info(msg, " : ", ret); - if (isError(ret)) throw new DatabaseException("error: " ~ msg); + if (isError(ret)) + throw new DatabaseException("error: " ~ msg); return ret; } struct Database { alias queryVariableType = QueryVariableType.QuestionMark; - static const FeatureArray features = [ - //Feature.InputBinding, - //Feature.DateBinding, - //Feature.ConnectionPool, - ]; + static const FeatureArray features = [//Feature.InputBinding, + //Feature.DateBinding, + //Feature.ConnectionPool, + ]; Allocator allocator; @@ -77,39 +73,26 @@ struct Driver(Policy) { //dbexit(); // should this be called (only on process exit?) } + static extern (C) int errorHandler(DBPROCESS* dbproc, int severity, + int dberr, int oserr, char* dberrstr, char* oserrstr) { - static extern(C) int errorHandler( - DBPROCESS* dbproc, - int severity, - int dberr, - int oserr, - char *dberrstr, - char *oserrstr) { - - auto con = cast(Connection *) dbgetuserdata(dbproc); + auto con = cast(Connection*) dbgetuserdata(dbproc); if (con) { con.error.message = to!string(dberrstr); } - info("error: ", - "severity: ", severity, - ", db error: ", to!string(dberrstr), - ", os error: ", to!string(oserrstr)); + info("error: ", "severity: ", severity, ", db error: ", + to!string(dberrstr), ", os error: ", to!string(oserrstr)); return INT_CANCEL; } - static extern(C) int msgHandler( - DBPROCESS *dbproc, - DBINT msgno, - int msgstate, - int severity, - char *msgtext, - char *srvname, - char *procname, - int line) { - - auto con = cast(Connection *) dbgetuserdata(dbproc); - if (con) {} + static extern (C) int msgHandler(DBPROCESS* dbproc, DBINT msgno, + int msgstate, int severity, char* msgtext, char* srvname, char* procname, + int line) { + + auto con = cast(Connection*) dbgetuserdata(dbproc); + if (con) { + } info("msg: ", to!string(msgtext), ", severity:", severity); return 0; } @@ -119,8 +102,8 @@ struct Driver(Policy) { struct Connection { Database* db; Source source; - LOGINREC *login; - DBPROCESS *con; + LOGINREC* login; + DBPROCESS* con; DatabaseError error; @@ -141,22 +124,26 @@ struct Driver(Policy) { string server; if (source.host.length != 0) { server = source.host ~ ":" ~ to!string(source.port); - } else { + } + else { server = source.server; } //con = dbopen(login, toStringz(source.server)); - con = check("tdsdbopen: " ~ server, tdsdbopen(login, toStringz(server), 1)); + con = check("tdsdbopen: " ~ server, tdsdbopen(login, toStringz(server), + 1)); - dbsetuserdata(con, cast(BYTE*) &this); + dbsetuserdata(con, cast(BYTE*)&this); check("dbuse", dbuse(con, toStringz(source.database))); } ~this() { //info("~Connection: ", source); - if (con) dbclose(con); - if (login) dbloginfree(login); + if (con) + dbclose(con); + if (login) + dbloginfree(login); } } @@ -164,7 +151,7 @@ struct Driver(Policy) { struct Statement { Connection* con; string sql; - Allocator *allocator; + Allocator* allocator; int binds; //Array!Bind inputbind_; @@ -187,35 +174,41 @@ struct Driver(Policy) { RETCODE status = check("dbsqlexec: ", dbsqlexec(con.con)); } - void query(X...) (X args) { + void query(X...)(X args) { bindAll(args); query(); } //bool hasRows() {return status != NO_MORE_RESULTS;} - bool hasRows() {return true;} + bool hasRows() { + return true; + } - private void bindAll(T...) (T args) { + private void bindAll(T...)(T args) { //int col; //foreach (arg; args) bind(++col, arg); } - void bind(int n, int value) {} - void bind(int n, const char[] value) {} + void bind(int n, int value) { + } + + void bind(int n, const char[] value) { + } void reset() { } private RETCODE check(string msg, RETCODE ret) { info(msg, " : ", ret); - if (isError(ret)) throw new DatabaseException(con.error, msg); + if (isError(ret)) + throw new DatabaseException(con.error, msg); return ret; } } struct Describe { - char *name; - char *buffer; + char* name; + char* buffer; int type; int size; int status; @@ -233,14 +226,19 @@ struct Driver(Policy) { //int columns() {return columns;} Statement* stmt; - Allocator *allocator; + Allocator* allocator; int columns; Array!Describe describe; Array!Bind bind; RETCODE status; - private auto con() {return stmt.con;} - private auto dbproc() {return con.con;} + private auto con() { + return stmt.con; + } + + private auto dbproc() { + return con.con; + } this(Statement* stmt_, int rowArraySize_) { stmt = stmt_; @@ -252,15 +250,16 @@ struct Driver(Policy) { } ~this() { - foreach(b; bind) allocator.deallocate(b.data); + foreach (b; bind) + allocator.deallocate(b.data); } void build_describe() { describe.reserve(columns); - for(int i = 0; i < columns; ++i) { - int c = i+1; + for (int i = 0; i < columns; ++i) { + int c = i + 1; describe ~= Describe(); auto d = &describe.back(); @@ -277,59 +276,53 @@ struct Driver(Policy) { bind.reserve(columns); - for(int i = 0; i < columns; ++i) { - int c = i+1; + for (int i = 0; i < columns; ++i) { + int c = i + 1; bind ~= Bind(); auto b = &bind.back(); auto d = &describe[i]; int allocSize; switch (d.type) { - case SYBCHAR: - b.type = ValueType.String; - b.size = 255; - allocSize = b.size+1; - b.bindType = NTBSTRINGBIND; - break; - case SYBMSDATE: - b.type = ValueType.Date; - b.size = DBDATETIME.sizeof; - allocSize = b.size; - b.bindType = DATETIMEBIND; - break; - default: - b.type = ValueType.String; - b.size = 255; - allocSize = b.size+1; - b.bindType = NTBSTRINGBIND; - break; + case SYBCHAR: + b.type = ValueType.String; + b.size = 255; + allocSize = b.size + 1; + b.bindType = NTBSTRINGBIND; + break; + case SYBMSDATE: + b.type = ValueType.Date; + b.size = DBDATETIME.sizeof; + allocSize = b.size; + b.bindType = DATETIMEBIND; + break; + default: + b.type = ValueType.String; + b.size = 255; + allocSize = b.size + 1; + b.bindType = NTBSTRINGBIND; + break; } b.data = allocator.allocate(allocSize); // make consistent acros dbs GC.addRange(b.data.ptr, b.data.length); - check("dbbind", dbbind( - dbproc, - c, - b.bindType, - cast(DBINT) b.data.length, - cast(BYTE*) b.data.ptr)); + check("dbbind", dbbind(dbproc, c, b.bindType, + cast(DBINT) b.data.length, cast(BYTE*) b.data.ptr)); check("dbnullbind", dbnullbind(dbproc, c, &b.status)); - info( - "output bind: index: ", i, - ", type: ", b.bindType, - ", size: ", b.size, - ", allocSize: ", b.data.length); + info("output bind: index: ", i, ", type: ", b.bindType, + ", size: ", b.size, ", allocSize: ", b.data.length); } } int fetch() { status = check("dbnextrow", dbnextrow(dbproc)); if (status == REG_ROW) { - return 1; - } else if (status == NO_MORE_ROWS) { + return 1; + } + else if (status == NO_MORE_ROWS) { stmt.reset(); return 0; } @@ -340,38 +333,38 @@ struct Driver(Policy) { return to!string(describe[idx].name); } - ubyte[] rawData(Cell* cell) { //TODO: fix - return null; - } - - Variant getValue(Cell* cell) - { - Variant value; - if(cell.bind.type == ValueType.Date) - { - auto ptr = cast(DBDATETIME*) cell.bind.data.ptr; - DBDATEREC d; - check("dbdatecrack", dbdatecrack(dbproc, &d, ptr)); - value = Date(d.year, d.month, d.day); - - } - else - { - import core.stdc.string: strlen; - checkType(cell.bind.bindType, NTBSTRINGBIND); - auto ptr = cast(immutable char*) cell.bind.data.ptr; - value = cast(string) ptr[0..strlen(ptr)]; - } - return value; - } - - bool isNull(Cell* cell){return false;} + ubyte[] rawData(Cell* cell) { //TODO: fix + return null; + } + + Variant getValue(Cell* cell) { + Variant value; + if (cell.bind.type == ValueType.Date) { + auto ptr = cast(DBDATETIME*) cell.bind.data.ptr; + DBDATEREC d; + check("dbdatecrack", dbdatecrack(dbproc, &d, ptr)); + value = Date(d.year, d.month, d.day); + + } + else { + import core.stdc.string : strlen; + + checkType(cell.bind.bindType, NTBSTRINGBIND); + auto ptr = cast(immutable char*) cell.bind.data.ptr; + value = cast(string) ptr[0 .. strlen(ptr)]; + } + return value; + } + + bool isNull(Cell* cell) { + return false; + } void checkType(int a, int b) { - if (a != b) throw new DatabaseException("type mismatch"); + if (a != b) + throw new DatabaseException("type mismatch"); } } } - diff --git a/src/std/database/front.d b/src/std/database/front.d index 3c3db01..d1aa62f 100644 --- a/src/std/database/front.d +++ b/src/std/database/front.d @@ -28,46 +28,65 @@ public import std.database.array; (version_major == 2 && version_minor == 70)); */ -struct Time -{ - this(uint h, uint m, uint s = 0, uint ms = 0) - { - hour = h; minute = m; second = s; msecond = ms; - } - - uint hour; - uint minute; - uint second; - uint msecond; +struct Time { + this(uint h, uint m, uint s = 0, uint ms = 0) { + hour = h; + minute = m; + second = s; + msecond = ms; + } + + uint hour; + uint minute; + uint second; + uint msecond; } enum ValueType { - Char, + Char, Short, - Int, - Long, + Int, + Long, - Float, - Double, + Float, + Double, String, Date, - Time, - DateTime, + Time, + DateTime, Raw, - Variant //TODO: remove + Variant //TODO: remove } // improve -struct TypeInfo(T:int) {static auto type() {return ValueType.Int;}} -struct TypeInfo(T:string) {static auto type() {return ValueType.String;}} -struct TypeInfo(T:Date) {static auto type() {return ValueType.Date;}} -struct TypeInfo(T:Variant) {static auto type() {return ValueType.Variant;}} +struct TypeInfo(T : int) { + static auto type() { + return ValueType.Int; + } +} + +struct TypeInfo(T : string) { + static auto type() { + return ValueType.String; + } +} + +struct TypeInfo(T : Date) { + static auto type() { + return ValueType.Date; + } +} +struct TypeInfo(T : Variant) { + static auto type() { + return ValueType.Variant; + } +} enum Feature { InputBinding, @@ -78,35 +97,60 @@ enum Feature { alias FeatureArray = Feature[]; -struct BasicDatabase(D,P) { +struct BasicDatabase(D, P) { alias Driver = D; alias Policy = P; alias Database = Driver.Database; alias Allocator = Policy.Allocator; - alias Connection = BasicConnection!(Driver,Policy); - alias Cell = BasicCell!(Driver,Policy); + alias Connection = BasicConnection!(Driver, Policy); + alias Cell = BasicCell!(Driver, Policy); alias Pool = .Pool!(Driver.Connection); alias ScopedResource = .ScopedResource!Pool; alias queryVariableType = Database.queryVariableType; - static auto create() {return BasicDatabase(null);} - static auto create(string url) {return BasicDatabase(url);} + static auto create() { + return BasicDatabase(null); + } - auto connection() {return Connection(this);} - auto connection(string uri) {return Connection(this, uri);} + static auto create(string url) { + return BasicDatabase(url); + } - auto statement(string sql) {return connection().statement(sql);} - auto statement(X...) (string sql, X args) {return connection.statement(sql,args);} + auto connection() { + return Connection(this); + } + + auto connection(string uri) { + return Connection(this, uri); + } - auto query(string sql) {return connection().query(sql);} - auto query(T...) (string sql, T args) {return statement(sql).query(args);} + auto statement(string sql) { + return connection().statement(sql); + } + + auto statement(X...)(string sql, X args) { + return connection.statement(sql, args); + } + + auto query(string sql) { + return connection().query(sql); + } + + auto query(T...)(string sql, T args) { + return statement(sql).query(args); + } //static bool hasFeature(Feature feature); // go with non-static hasFeature for now to accomidate poly driver - bool hasFeature(Feature feature) {return hasFeature(data_.database, feature);} -auto ref driverDatabase() {return data_.database;} + bool hasFeature(Feature feature) { + return hasFeature(data_.database, feature); + } + + auto ref driverDatabase() { + return data_.database; + } private struct Payload { string defaultURI; @@ -130,12 +174,12 @@ auto ref driverDatabase() {return data_.database;} // for poly static if (hasMember!(Database, "register")) { - static void register(DB) (string name = "") { - Database.register!DB(name); + static void register(DB)(string name = "") { + Database.register!DB(name); } auto database(string name) { - auto db = BasicDatabase(data_.defaultURI); + auto db = BasicDatabase(data_.defaultURI); db.data_.database.setDatabase(name); return db; } @@ -143,27 +187,39 @@ auto ref driverDatabase() {return data_.database;} private static bool hasFeature(ref Database db, Feature feature) { import std.algorithm; - import std.range.primitives: empty; + import std.range.primitives : empty; + //auto r = find(Database.features, feature); auto r = find(db.features, feature); return !r.empty; } } -struct BasicConnection(D,P) { +struct BasicConnection(D, P) { alias Driver = D; alias Policy = P; alias DriverConnection = Driver.Connection; - alias Statement = BasicStatement!(Driver,Policy); - alias Database = BasicDatabase!(Driver,Policy); + alias Statement = BasicStatement!(Driver, Policy); + alias Database = BasicDatabase!(Driver, Policy); alias Pool = Database.Pool; alias ScopedResource = Database.ScopedResource; alias DatabaseImpl = Driver.Database; - auto statement(string sql) {return Statement(this,sql);} - auto statement(X...) (string sql, X args) {return Statement(this,sql,args);} - auto query(string sql) {return statement(sql).query();} - auto query(T...) (string sql, T args) {return statement(sql).query(args);} + auto statement(string sql) { + return Statement(this, sql); + } + + auto statement(X...)(string sql, X args) { + return Statement(this, sql, args); + } + + auto query(string sql) { + return statement(sql).query(); + } + + auto query(T...)(string sql, T args) { + return statement(sql).query(args); + } auto rowArraySize(int rows) { rowArraySize_ = rows; @@ -178,7 +234,9 @@ struct BasicConnection(D,P) { string uri_; int rowArraySize_ = 1; - package this(Database db) {this(db,"");} + package this(Database db) { + this(db, ""); + } package this(Database db, string uri) { //db_ = db; @@ -209,7 +267,9 @@ struct BasicConnection(D,P) { } static if (hasMember!(Database, "socket")) { - auto socket() {return driverConnection.socket;} + auto socket() { + return driverConnection.socket; + } } // handle() @@ -217,7 +277,9 @@ struct BasicConnection(D,P) { // while a handle is in use (Use a connection variable to extend scope) static if (hasMember!(Database, "handle")) { - auto handle() {return driverConnection.handle;} + auto handle() { + return driverConnection.handle; + } } /* @@ -227,17 +289,16 @@ data_ = Data(&db.data_.refCountedPayload(),uri); } */ - } -struct BasicStatement(D,P) { +struct BasicStatement(D, P) { alias Driver = D; alias Policy = P; alias DriverStatement = Driver.Statement; - alias Connection = BasicConnection!(Driver,Policy); - alias Result = BasicResult!(Driver,Policy); - alias RowSet = BasicRowSet!(Driver,Policy); - alias ColumnSet = BasicColumnSet!(Driver,Policy); + alias Connection = BasicConnection!(Driver, Policy); + alias Result = BasicResult!(Driver, Policy); + alias RowSet = BasicRowSet!(Driver, Policy); + alias ColumnSet = BasicColumnSet!(Driver, Policy); //alias Allocator = Policy.Allocator; alias ScopedResource = Connection.ScopedResource; @@ -255,10 +316,9 @@ struct BasicStatement(D,P) { Executed, } - this(Connection con, string sql) { con_ = con; - data_ = Data(con_.driverConnection,sql); + data_ = Data(con_.driverConnection, sql); rowArraySize_ = con.rowArraySize_; prepare(); } @@ -273,11 +333,18 @@ struct BasicStatement(D,P) { } */ - string sql() {return data_.sql;} + string sql() { + return data_.sql; + } //int binds() {return data_.binds;} - void bind(int n, int value) {data_.bind(n, value);} - void bind(int n, const char[] value){data_.bind(n,value);} + void bind(int n, int value) { + data_.bind(n, value); + } + + void bind(int n, const char[] value) { + data_.bind(n, value); + } auto query() { data_.query(); @@ -285,19 +352,22 @@ struct BasicStatement(D,P) { return this; } - auto query(X...) (X args) { + auto query(X...)(X args) { data_.query(args); state = State.Executed; return this; } - auto into(A...) (ref A args) { - if (state != State.Executed) throw new DatabaseException("not executed"); + auto into(A...)(ref A args) { + if (state != State.Executed) + throw new DatabaseException("not executed"); rows.into(args); return this; } - bool hasRows() {return data_.hasRows;} + bool hasRows() { + return data_.hasRows; + } // rows() // accessor for single rowSet returned @@ -305,24 +375,26 @@ struct BasicStatement(D,P) { // alternate name: rowSet, table auto rows() { - if (state != State.Executed) query(); + if (state != State.Executed) + query(); return RowSet(Result(this, rowArraySize_)); } auto columns() { - if (state != State.Executed) query(); + if (state != State.Executed) + query(); return ColumnSet(Result(this, rowArraySize_)); } // results() // returns range for one or more things returned by a query auto results() { - if (state != State.Executed) throw new DatabaseException("not executed"); + if (state != State.Executed) + throw new DatabaseException("not executed"); return 0; // fill in } - - private: +private: alias RefCounted!(DriverStatement, RefCountedAutoInitialize.no) Data; Data data_; @@ -335,16 +407,17 @@ struct BasicStatement(D,P) { state = State.Prepared; } - void reset() {data_.reset();} //SQLCloseCursor + void reset() { + data_.reset(); + } //SQLCloseCursor } - -struct BasicResult(D,P) { +struct BasicResult(D, P) { alias Driver = D; alias Policy = P; alias ResultImpl = Driver.Result; - alias Statement = BasicStatement!(Driver,Policy); - alias RowSet = BasicRowSet!(Driver,Policy); + alias Statement = BasicStatement!(Driver, Policy); + alias RowSet = BasicRowSet!(Driver, Policy); //alias Allocator = Driver.Policy.Allocator; alias Bind = Driver.Bind; //alias Row = .Row; @@ -353,7 +426,8 @@ struct BasicResult(D,P) { stmt_ = stmt; rowArraySize_ = rowArraySize; data_ = Data(&stmt.data_.refCountedPayload(), rowArraySize_); - if (!stmt_.hasRows) throw new DatabaseException("not a result query"); + if (!stmt_.hasRows) + throw new DatabaseException("not a result query"); rowsFetched_ = data_.fetch(); } @@ -361,20 +435,25 @@ struct BasicResult(D,P) { //auto opSlice() {return ResultRange(this);} package: - int rowsFetched() {return rowsFetched_;} + int rowsFetched() { + return rowsFetched_; + } bool next() { if (++rowIdx_ == rowsFetched_) { rowsFetched_ = data_.fetch(); - if (!rowsFetched_) return false; + if (!rowsFetched_) + return false; rowIdx_ = 0; } return true; } - auto ref result() {return data_.refCountedPayload();} + auto ref result() { + return data_.refCountedPayload(); + } - private: +private: Statement stmt_; int rowArraySize_; //maybe move into RC int rowIdx_; @@ -384,48 +463,66 @@ package: Data data_; // these need to move - int width() {return data_.columns;} + int width() { + return data_.columns; + } private size_t index(string name) { import std.uni; + // slow name lookup, all case insensitive for now - for(int i=0; i!=width; i++) { - if (sicmp(data_.name(i), name) == 0) return i; + for (int i = 0; i != width; i++) { + if (sicmp(data_.name(i), name) == 0) + return i; } throw new DatabaseException("column name not found:" ~ name); } } -struct BasicColumnSet(D,P) { +struct BasicColumnSet(D, P) { alias Driver = D; alias Policy = P; - alias Result = BasicResult!(Driver,Policy); - alias Column = BasicColumn!(Driver,Policy); + alias Result = BasicResult!(Driver, Policy); + alias Column = BasicColumn!(Driver, Policy); private Result result_; this(Result result) { result_ = result; } - int width() {return result_.data_.columns;} + int width() { + return result_.data_.columns; + } struct Range { private Result result_; int idx; - this(Result result) {result_ = result;} - bool empty() {return idx == result_.data_.columns;} - auto front() {return Column(result_, idx);} - void popFront() {++idx;} + this(Result result) { + result_ = result; + } + + bool empty() { + return idx == result_.data_.columns; + } + + auto front() { + return Column(result_, idx); + } + + void popFront() { + ++idx; + } } - auto opSlice() {return Range(result_);} + auto opSlice() { + return Range(result_); + } } - -struct BasicColumn(D,P) { +struct BasicColumn(D, P) { alias Driver = D; alias Policy = P; - alias Result = BasicResult!(Driver,Policy); + alias Result = BasicResult!(Driver, Policy); private Result result_; private size_t idx_; @@ -434,18 +531,22 @@ struct BasicColumn(D,P) { idx_ = idx; } - auto idx() {return idx_;} - auto name() {return result_.data_.name(idx_);} + auto idx() { + return idx_; + } -} + auto name() { + return result_.data_.name(idx_); + } +} -struct BasicRowSet(D,P) { +struct BasicRowSet(D, P) { alias Driver = D; alias Policy = P; - alias Result = BasicResult!(Driver,Policy); - alias Row = BasicRow!(Driver,Policy); - alias ColumnSet = BasicColumnSet!(Driver,Policy); + alias Result = BasicResult!(Driver, Policy); + alias Row = BasicRow!(Driver, Policy); + alias ColumnSet = BasicColumnSet!(Driver, Policy); void rowSetTag(); @@ -455,21 +556,25 @@ struct BasicRowSet(D,P) { result_ = result; } - int width() {return result_.data_.columns;} + int width() { + return result_.data_.columns; + } // length will be for the number of rows (if defined) int length() { throw new Exception("not a completed/detached rowSet"); } - auto into(A...) (ref A args) { - if (!result_.rowsFetched()) throw new DatabaseException("no data"); + auto into(A...)(ref A args) { + if (!result_.rowsFetched()) + throw new DatabaseException("no data"); auto row = front(); - foreach(i, ref a; args) { + foreach (i, ref a; args) { alias T = A[i]; static if (is(T == string)) { a = row[i].as!T.dup; - } else { + } + else { a = row[i].as!T; } } @@ -481,43 +586,53 @@ struct BasicRowSet(D,P) { return this; } - // into for output range: experimental //if (isOutputRange!(R,E)) - auto into(R) (R range) - if (hasMember!(R, "put")) { - // Row should have range - // needs lots of work - auto row = Row(this); - for(int i=0; i != width; i++) { - auto f = row[i]; - switch (f.type) { - case ValueType.Int: put(range, f.as!int); break; - case ValueType.String: put(range, f.as!string); break; - //case ValueType.Date: put(range, f.as!Date); break; - default: throw new DatabaseException("switch error"); - } + auto into(R)(R range) if (hasMember!(R, "put")) { + // Row should have range + // needs lots of work + auto row = Row(this); + for (int i = 0; i != width; i++) { + auto f = row[i]; + switch (f.type) { + case ValueType.Int: + put(range, f.as!int); + break; + case ValueType.String: + put(range, f.as!string); + break; + //case ValueType.Date: put(range, f.as!Date); break; + default: + throw new DatabaseException("switch error"); } } + } auto columns() { return ColumnSet(result_); } + bool empty() { + return result_.rowsFetched_ == 0; + } + + auto front() { + return Row(this); + } - bool empty() {return result_.rowsFetched_ == 0;} - auto front() {return Row(this);} - void popFront() {result_.next();} + void popFront() { + result_.next(); + } } -struct BasicRow(D,P) { +struct BasicRow(D, P) { alias Driver = D; alias Policy = P; - alias Result = BasicResult!(Driver,Policy); - alias RowSet = BasicRowSet!(Driver,Policy); - alias Cell = BasicCell!(Driver,Policy); - alias Value = BasicValue!(Driver,Policy); - alias Column = BasicColumn!(Driver,Policy); + alias Result = BasicResult!(Driver, Policy); + alias RowSet = BasicRowSet!(Driver, Policy); + alias Cell = BasicCell!(Driver, Policy); + alias Value = BasicValue!(Driver, Policy); + alias Column = BasicColumn!(Driver, Policy); private RowSet rows_; @@ -525,14 +640,18 @@ struct BasicRow(D,P) { rows_ = rows; } - int width() {return rows_.result_.data_.columns;} + int width() { + return rows_.result_.data_.columns; + } - auto into(A...) (ref A args) { + auto into(A...)(ref A args) { rows_.into(args); return this; } - auto opIndex(Column column) {return opIndex(column.idx);} + auto opIndex(Column column) { + return opIndex(column.idx); + } // experimental auto opDispatch(string s)() { @@ -543,162 +662,188 @@ struct BasicRow(D,P) { auto result = &rows_.result_; // needs work // sending a ptr to cell instead of reference (dangerous) - return Value(Cell(result, &result.data_.bind[idx], idx)); + return Value(Cell(result, &result.data_.bind[idx], idx)); } } - -struct BasicValue(D,P) { +struct BasicValue(D, P) { alias Driver = D; alias Policy = P; //alias Result = Driver.Result; - alias Result = BasicResult!(Driver,Policy); - alias Cell = BasicCell!(Driver,Policy); + alias Result = BasicResult!(Driver, Policy); + alias Cell = BasicCell!(Driver, Policy); alias Bind = Driver.Bind; private Cell cell_; - private Variant data_; - alias Converter = .Converter!(Driver,Policy); + private Variant data_; + alias Converter = .Converter!(Driver, Policy); this(Cell cell) { cell_ = cell; - data_ = resultPtr.getValue(&cell_); + data_ = resultPtr.getValue(&cell_); } private auto resultPtr() { - auto r = cell_.result_; - return &(r.result()); // last parens matter here (something about delegate) + auto r = cell_.result_; + return &(r.result()); // last parens matter here (something about delegate) } - Variant value(){return data_;} + Variant value() { + return data_; + } - ubyte[] rawData(){return resultPtr.rawData(&cell_);} + ubyte[] rawData() { + return resultPtr.rawData(&cell_); + } - auto type() {return cell_.bind.type;} + auto type() { + return cell_.bind.type; + } - auto as(T)(){return data_.coerce!T();} + auto as(T)() { + return data_.coerce!T(); + } - auto as(T:Variant)() {return data_;}//Converter.convert!T(resultPtr, cell_);} + auto as(T : Variant)() { + return data_; + } //Converter.convert!T(resultPtr, cell_);} - bool isNull() {return resultPtr.isNull(&cell_);} //fix + bool isNull() { + return resultPtr.isNull(&cell_); + } //fix - string name() {return resultPtr.name(cell_.idx_);} + string name() { + return resultPtr.name(cell_.idx_); + } - auto get(T)() {if(data_.convertsTo!T()) return Nullable!T(as!T); else return Nullable!T;} + auto get(T)() { + if (data_.convertsTo!T()) + return Nullable!T(as!T); + else + return Nullable!T; + } // not sure if this does anything //const(char)[] chars() {return as!string;} - string toString() {return data_.toString();} + string toString() { + return data_.toString(); + } } -struct BasicCell(D,P) { +struct BasicCell(D, P) { alias Driver = D; alias Policy = P; - alias Result = BasicResult!(Driver,Policy); + alias Result = BasicResult!(Driver, Policy); alias Bind = Driver.Bind; - alias Value = BasicValue!(Driver,Policy); + alias Value = BasicValue!(Driver, Policy); private Bind* bind_; private int rowIdx_; private size_t idx_; - private Result * result_; + private Result* result_; - this(Result *r, Bind *b, size_t idx) { + this(Result* r, Bind* b, size_t idx) { bind_ = b; - result_ = r; + result_ = r; rowIdx_ = r.rowIdx_; idx_ = idx; } - private auto resultPtr() { - return &result_.result(); // last parens matter here (something about delegate) - } + private auto resultPtr() { + return &result_.result(); // last parens matter here (something about delegate) + } + + auto value() { + return Value(this); + } - auto value(){return Value(this);} + auto bind() { + return bind_; + } - auto bind() {return bind_;} - auto rowIdx() {return rowIdx_;} + auto rowIdx() { + return rowIdx_; + } } //Converter now is not used, but if del it ,it will build error. -struct Converter(D,P) { +struct Converter(D, P) { alias Driver = D; alias Policy = P; alias Result = Driver.Result; -// alias Bind = Driver.Bind; - alias Cell = BasicCell!(Driver,Policy); + // alias Bind = Driver.Bind; + alias Cell = BasicCell!(Driver, Policy); - // static Y convert(Y)(Result *r, ref Cell cell) { - /* ValueType x = cell.bind.type, y = TypeInfo!Y.type; + // static Y convert(Y)(Result *r, ref Cell cell) { + /* ValueType x = cell.bind.type, y = TypeInfo!Y.type; if (x == y) return r.get!Y(&cell); // temporary auto e = lookup(x,y); if (!e) conversionError(x,y); Y value; e.convert(r, &cell, &value);*/ -// return Y.init; -// } + // return Y.init; + // } - // static Y convert(Y:Variant)(Result *r, ref Cell cell) { - //return Y(123); - /* ValueType x = cell.bind.type, y = ValueType.Variant; + // static Y convert(Y:Variant)(Result *r, ref Cell cell) { + //return Y(123); + /* ValueType x = cell.bind.type, y = ValueType.Variant; auto e = lookup(x,y); if (!e) conversionError(x,y); Y value; e.convert(r, &cell, &value);*/ -// return Variant(); - // } + // return Variant(); + // } - // static Y convertDirect(Y)(Result *r, ref Cell cell) { - // assert(b.type == TypeInfo!Y.type); -// return Y.init;//return r.get!Y(&cell); - // } + // static Y convertDirect(Y)(Result *r, ref Cell cell) { + // assert(b.type == TypeInfo!Y.type); + // return Y.init;//return r.get!Y(&cell); + // } - // private: + // private: struct Elem { - ValueType from,to; - void function(Result*,void*,void*) convert; + ValueType from, to; + void function(Result*, void*, void*) convert; } // only cross converters, todo: all converters - static Elem[1] converters = [ - // {from: ValueType.Int, to: ValueType.String, &generate!(int,string).convert}, - // {from: ValueType.String, to: ValueType.Int, &generate!(string,int).convert}, + static Elem[1] converters = [// {from: ValueType.Int, to: ValueType.String, &generate!(int,string).convert}, + // {from: ValueType.String, to: ValueType.Int, &generate!(string,int).convert}, //{from: ValueType.Date, to: ValueType.String, &generate!(Date,string).convert}, // variants - {from: ValueType.Int, to: ValueType.Variant, &generate!(int,Variant).convert}, - //{from: ValueType.String, to: ValueType.Variant, &generate!(string,Variant).convert}, - //{from: ValueType.Date, to: ValueType.Variant, &generate!(Date,Variant).convert}, - ]; + { + from: + ValueType.Int, to : ValueType.Variant, &generate!(int, Variant).convert},//{from: ValueType.String, to: ValueType.Variant, &generate!(string,Variant).convert}, + //{from: ValueType.Date, to: ValueType.Variant, &generate!(Date,Variant).convert}, + ]; - // static Elem* lookup(ValueType x, ValueType y) { + // static Elem* lookup(ValueType x, ValueType y) { // rework into efficient array lookup - // foreach(ref i; converters) { - // if (i.from == x && i.to == y) return &i; - // } - // return null; - // } - - struct generate(X,Y) { - static void convert(Result *r, void *x_, void *y_) { - // import std.conv; - Cell* cell = cast(Cell*) x_; - // *cast(Y*) y_ = to!Y(r.get!X(cell)); + // foreach(ref i; converters) { + // if (i.from == x && i.to == y) return &i; + // } + // return null; + // } + + struct generate(X, Y) { + static void convert(Result* r, void* x_, void* y_) { + // import std.conv; + Cell* cell = cast(Cell*) x_; + // *cast(Y*) y_ = to!Y(r.get!X(cell)); + } } - } - // struct generate(X,Y:Variant) { - // static void convert(Result *r, void *x_, void *y_) { - // Cell* cell = cast(Cell*) x_; - // *cast(Y*) y_ = r.get!X(cell); - // } - // } - - // static void conversionError(ValueType x, ValueType y) { - // import std.conv; - // string msg; - // msg ~= "unsupported conversion from: " ~ to!string(x) ~ " to " ~ to!string(y); - // throw new DatabaseException(msg); - // } -} + // struct generate(X,Y:Variant) { + // static void convert(Result *r, void *x_, void *y_) { + // Cell* cell = cast(Cell*) x_; + // *cast(Y*) y_ = r.get!X(cell); + // } + // } + // static void conversionError(ValueType x, ValueType y) { + // import std.conv; + // string msg; + // msg ~= "unsupported conversion from: " ~ to!string(x) ~ " to " ~ to!string(y); + // throw new DatabaseException(msg); + // } + } diff --git a/src/std/database/mysql/database.d b/src/std/database/mysql/database.d index 90fc31d..6bee8e4 100644 --- a/src/std/database/mysql/database.d +++ b/src/std/database/mysql/database.d @@ -7,18 +7,14 @@ import std.stdio; import std.database.common; import std.database.source; -version (Windows) -{ +version (Windows) { pragma(lib, "libmysql"); } -else -{ - version (webscalesql) - { +else { + version (webscalesql) { pragma(lib, "webscalesql"); } - else - { + else { pragma(lib, "mysqlclient"); } } @@ -37,72 +33,60 @@ alias Database(T) = BasicDatabase!(Driver!T.Sync, T); alias AsyncDatabase(T) = BasicDatabase!(Driver!T.Async, T); -struct DefaultPolicy -{ +struct DefaultPolicy { alias Allocator = MyMallocator; } -auto createDatabase()(string uri = "") -{ +auto createDatabase()(string uri = "") { return Database!DefaultPolicy(uri); } -auto createDatabase(T)(string uri = "") -{ +auto createDatabase(T)(string uri = "") { return Database!T(uri); } -private static bool isError()(int ret) -{ +private static bool isError()(int ret) { return !(ret == 0 || ret == MYSQL_NO_DATA || ret == MYSQL_DATA_TRUNCATED); } -private static T* check(T)(string msg, T* ptr) -{ +private static T* check(T)(string msg, T* ptr) { info(msg, ":", ptr); if (!ptr) raiseError(msg); return ptr; } -private static int check()(string msg, MYSQL_STMT* stmt, int ret) -{ +private static int check()(string msg, MYSQL_STMT* stmt, int ret) { info(msg, ":", ret); if (isError(ret)) raiseError(msg, stmt, ret); return ret; } -private static void raiseError()(string msg) -{ +private static void raiseError()(string msg) { throw new DatabaseException("mysql error: " ~ msg); } -private static void raiseError()(string msg, int ret) -{ +private static void raiseError()(string msg, int ret) { throw new DatabaseException("mysql error: status: " ~ to!string(ret) ~ ":" ~ msg); } -private static void raiseError()(string msg, MYSQL_STMT* stmt, int ret) -{ +private static void raiseError()(string msg, MYSQL_STMT* stmt, int ret) { import core.stdc.string : strlen; const(char*) err = mysql_stmt_error(stmt); throw new DatabaseException("mysql error: " ~ msg); } -private struct Driver(Policy) -{ +private struct Driver(Policy) { - struct Describe - { + struct Describe { int index; immutable(char)[] name; MYSQL_FIELD* field; } - struct Bind - { + struct Bind { ValueType type; int mysql_type; int allocSize; @@ -112,8 +96,7 @@ private struct Driver(Policy) my_bool error; } - struct Sync - { + struct Sync { alias Allocator = Policy.Allocator; alias Cell = BasicCell!(Sync, Policy); alias const(ubyte)* cstring; @@ -121,8 +104,7 @@ private struct Driver(Policy) alias Describe = Driver!Policy.Describe; alias Bind = Driver!Policy.Bind; - struct Database - { + struct Database { alias queryVariableType = QueryVariableType.QuestionMark; static const FeatureArray features = [Feature.InputBinding, Feature.DateBinding, @@ -131,25 +113,21 @@ private struct Driver(Policy) Allocator allocator; - this(string uri) - { + this(string uri) { allocator = Allocator(); info("mysql client info: ", to!string(mysql_get_client_info())); } - ~this() - { + ~this() { log("~Database"); } } - struct Connection - { + struct Connection { Database* db; MYSQL* mysql; - this(Database* db_, Source source) - { + this(Database* db_, Source source) { db = db_; mysql = check("mysql_init", mysql_init(null)); @@ -161,8 +139,7 @@ private struct Driver(Policy) cast(cstring) toStringz(source.database), 0, null, 0)); } - ~this() - { + ~this() { log("~Statement"); if (mysql) mysql_close(mysql); @@ -171,18 +148,16 @@ private struct Driver(Policy) } - static void bindSetup(ref Array!Bind bind, ref Array!MYSQL_BIND mysqlBind) - { + static void bindSetup(ref Array!Bind bind, ref Array!MYSQL_BIND mysqlBind) { // make this efficient mysqlBind.clear(); mysqlBind.reserve(bind.length); - for (int i = 0; i != bind.length; ++i) - { + for (int i = 0; i != bind.length; ++i) { mysqlBind ~= MYSQL_BIND(); auto b = &bind[i]; auto mb = &mysqlBind[i]; - //import core.stdc.string: memset; - //memset(mb, 0, MYSQL_BIND.sizeof); // might not be needed: D struct + //import core.stdc.string: memset; + //memset(mb, 0, MYSQL_BIND.sizeof); // might not be needed: D struct mb.buffer_type = b.mysql_type; mb.buffer = b.data.ptr; mb.buffer_length = b.allocSize; @@ -192,8 +167,7 @@ private struct Driver(Policy) } } - struct Statement - { + struct Statement { Connection* con; string sql; Allocator* allocator; @@ -203,16 +177,14 @@ private struct Driver(Policy) Array!MYSQL_BIND mysqlBind; bool bindInit; - this(Connection* con_, string sql_) - { + this(Connection* con_, string sql_) { con = con_; sql = sql_; allocator = &con.db.allocator; stmt = check("mysql_stmt_init", mysql_stmt_init(con.mysql)); } - ~this() - { + ~this() { log("~Statement"); foreach (b; inputBind) allocator.deallocate(b.data); @@ -222,28 +194,23 @@ private struct Driver(Policy) } // hoist? - this(this) - { + this(this) { assert(false); } - void opAssign(Statement rhs) - { + void opAssign(Statement rhs) { assert(false); } - void prepare() - { + void prepare() { check("mysql_stmt_prepare", stmt, mysql_stmt_prepare(stmt, cast(char*) sql.ptr, sql.length)); binds = cast(uint) mysql_stmt_param_count(stmt); } - void query() - { - if (inputBind.length && !bindInit) - { + void query() { + if (inputBind.length && !bindInit) { bindInit = true; bindSetup(inputBind, mysqlBind); @@ -256,30 +223,25 @@ private struct Driver(Policy) check("mysql_stmt_execute", stmt, mysql_stmt_execute(stmt)); } - void query(X...)(X args) - { + void query(X...)(X args) { bindAll(args); query(); } - bool hasRows() - { + bool hasRows() { return true; } - void reset() - { + void reset() { } - private void bindAll(T...)(T args) - { + private void bindAll(T...)(T args) { int col; foreach (arg; args) bind(++col, arg); } - void bind(int n, int value) - { + void bind(int n, int value) { info("input bind: n: ", n, ", value: ", value); auto b = bindAlloc(n, MYSQL_TYPE_LONG, int.sizeof); b.is_null = 0; @@ -288,8 +250,7 @@ private struct Driver(Policy) } - void bind(int n, const char[] value) - { + void bind(int n, const char[] value) { import core.stdc.string : strncpy; info("input bind: n: ", n, ", value: ", value); @@ -304,8 +265,7 @@ private struct Driver(Policy) b.length = value.length; } - void bind(int n, Date d) - { + void bind(int n, Date d) { auto b = bindAlloc(n, MYSQL_TYPE_DATE, MYSQL_TIME.sizeof); b.is_null = 0; b.error = 0; @@ -316,8 +276,7 @@ private struct Driver(Policy) p.day = d.day; } - Bind* bindAlloc(int n, int mysql_type, int allocSize) - { + Bind* bindAlloc(int n, int mysql_type, int allocSize) { if (n == 0) throw new DatabaseException("zero index"); auto idx = n - 1; @@ -336,8 +295,7 @@ private struct Driver(Policy) } - struct Result - { + struct Result { Statement* stmt; Allocator* allocator; uint columns; @@ -349,8 +307,7 @@ private struct Driver(Policy) static const maxData = 256; - this(Statement* stmt_, int rowArraySize_) - { + this(Statement* stmt_, int rowArraySize_) { stmt = stmt_; allocator = stmt.allocator; @@ -363,8 +320,7 @@ private struct Driver(Policy) build_bind(); } - ~this() - { + ~this() { log("~Result"); foreach (b; bind) allocator.deallocate(b.data); @@ -373,16 +329,14 @@ private struct Driver(Policy) log("~Result"); } - void build_describe() - { + void build_describe() { import core.stdc.string : strlen; columns = cast(uint) mysql_stmt_field_count(stmt.stmt); describe.reserve(columns); - for (int i = 0; i != columns; ++i) - { + for (int i = 0; i != columns; ++i) { describe ~= Describe(); auto d = &describe.back(); @@ -396,22 +350,19 @@ private struct Driver(Policy) } } - void build_bind() - { + void build_bind() { import core.stdc.string : memset; import core.memory : GC; bind.reserve(columns); - for (int i = 0; i != columns; ++i) - { + for (int i = 0; i != columns; ++i) { auto d = &describe[i]; bind ~= Bind(); auto b = &bind.back(); b.mysql_type = d.field.type; - b.allocSize = cast(uint)(d.field.length + 1); - switch (d.field.type) - { + b.allocSize = cast(uint)(d.field.length + 1); + switch (d.field.type) { case MYSQL_TYPE_TINY: b.type = ValueType.Char; break; @@ -437,19 +388,19 @@ private struct Driver(Policy) case MYSQL_TYPE_TIME: case MYSQL_TYPE_TIME2: b.type = ValueType.Time; - b.allocSize += 30; + b.allocSize += 30; break; case MYSQL_TYPE_DATETIME2: case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_TIMESTAMP2: case MYSQL_TYPE_TIMESTAMP: b.type = ValueType.DateTime; - b.allocSize += 30; + b.allocSize += 30; break; default: b.mysql_type = MYSQL_TYPE_STRING; b.type = ValueType.String; - b.allocSize += 256; + b.allocSize += 256; break; } @@ -472,25 +423,20 @@ private struct Driver(Policy) mysql_stmt_bind_result(stmt.stmt, &mysqlBind.front()); } - bool hasResult() - { + bool hasResult() { return result_metadata != null; } - int fetch() - { + int fetch() { status = check("mysql_stmt_fetch", stmt.stmt, mysql_stmt_fetch(stmt.stmt)); - if (!status) - { + if (!status) { return 1; } - else if (status == MYSQL_NO_DATA) - { + else if (status == MYSQL_NO_DATA) { //rows_ = row_count_; return 0; } - else if (status == MYSQL_DATA_TRUNCATED) - { + else if (status == MYSQL_DATA_TRUNCATED) { raiseError("mysql_stmt_fetch: truncation", status); } @@ -500,68 +446,56 @@ private struct Driver(Policy) // value getters - Variant getValue(Cell* cell) - { + Variant getValue(Cell* cell) { Variant value; - switch (cell.bind.type) - { - case ValueType.Char: - { + switch (cell.bind.type) { + case ValueType.Char: { auto t = (*cast(char*) cell.bind.data.ptr); value = t; } break; - case ValueType.Short: - { + case ValueType.Short: { auto t = (*cast(short*) cell.bind.data.ptr); value = t; } break; - case ValueType.Int: - { + case ValueType.Int: { auto t = (*cast(int*) cell.bind.data.ptr); value = t; } break; - case ValueType.Long: - { + case ValueType.Long: { auto t = (*cast(long*) cell.bind.data.ptr); value = t; } break; - case ValueType.Float: - { + case ValueType.Float: { auto t = (*cast(float*) cell.bind.data.ptr); value = t; } break; - case ValueType.Double: - { + case ValueType.Double: { auto t = (*cast(double*) cell.bind.data.ptr); value = t; } break; - case ValueType.String: - { + case ValueType.String: { auto ptr = cast(char*) cell.bind.data.ptr; value = cast(string)(ptr[0 .. cell.bind.length]); } break; - case ValueType.Date: - { + case ValueType.Date: { MYSQL_TIME* t = cast(MYSQL_TIME*) cell.bind.data.ptr; value = Date(t.year, t.month, t.day); } break; - case ValueType.Time: - { + case ValueType.Time: { MYSQL_TIME* t = cast(MYSQL_TIME*) cell.bind.data.ptr; value = Time(t.hour, t.minute, t.second); } break; - case ValueType.DateTime: - { + case ValueType.DateTime: { MYSQL_TIME* t = cast(MYSQL_TIME*) cell.bind.data.ptr; value = DateTime(t.year, t.month, t.day, t.hour, t.minute, t.second); @@ -579,25 +513,22 @@ private struct Driver(Policy) return ptr[0..b.length]; } */ - ubyte[] rawData(Cell* cell) - { - log("-----------bind.bufferLength = ", cell.bind.allocSize , " , dataLength = ", cell.bind.length); + ubyte[] rawData(Cell* cell) { + log("-----------bind.bufferLength = ", cell.bind.allocSize, + " , dataLength = ", cell.bind.length); auto ptr = cast(ubyte*) cell.bind.data.ptr; - return ptr[0 .. cell.bind.length]; + return ptr[0 .. cell.bind.length]; } - bool isNull(Cell* cell) - { + bool isNull(Cell* cell) { return cell.bind.is_null > 0; } - auto name(size_t idx) - { + auto name(size_t idx) { return describe[idx].name; } - static void checkType(T)(Bind* b) - { + static void checkType(T)(Bind* b) { int x = TypeInfo!T.type(); int y = b.mysql_type; if (x == y) @@ -607,26 +538,20 @@ private struct Driver(Policy) } // refactor as a better 1-n bind mapping - struct TypeInfo(T : int) - { - static int type() - { + struct TypeInfo(T : int) { + static int type() { return MYSQL_TYPE_LONG; } } - struct TypeInfo(T : string) - { - static int type() - { + struct TypeInfo(T : string) { + static int type() { return MYSQL_TYPE_STRING; } } - struct TypeInfo(T : Date) - { - static int type() - { + struct TypeInfo(T : Date) { + static int type() { return MYSQL_TYPE_DATE; } } @@ -634,49 +559,41 @@ private struct Driver(Policy) } } - struct Async - { + struct Async { alias Allocator = Policy.Allocator; alias Describe = Driver!Policy.Describe; alias Bind = Driver!Policy.Bind; alias Cell = BasicCell!(Async, Policy); - struct Database - { + struct Database { alias queryVariableType = QueryVariableType.QuestionMark; static const FeatureArray features = [Feature.InputBinding, Feature.DateBinding, //Feature.ConnectionPool, ]; - this(string defaultURI) - { + this(string defaultURI) { info("mysql client info: ", to!string(mysql_get_client_info())); } - bool bindable() - { + bool bindable() { return true; } - bool dateBinding() - { + bool dateBinding() { return true; } - bool poolEnable() - { + bool poolEnable() { return false; } } - struct Connection - { + struct Connection { Database* db; MYSQL* mysql; - this(Database* db_, Source source) - { + this(Database* db_, Source source) { db = db_; mysql = check("mysql_init", mysql_init(null)); @@ -688,8 +605,7 @@ private struct Driver(Policy) cast(cstring) toStringz(source.database), 0, null, 0)); } - ~this() - { + ~this() { log("~Statement"); if (mysql) mysql_close(mysql); @@ -698,13 +614,11 @@ private struct Driver(Policy) } - static void bindSetup(ref Array!Bind bind, ref Array!MYSQL_BIND mysqlBind) - { + static void bindSetup(ref Array!Bind bind, ref Array!MYSQL_BIND mysqlBind) { // make this efficient mysqlBind.clear(); mysqlBind.reserve(bind.length); - for (int i = 0; i != bind.length; ++i) - { + for (int i = 0; i != bind.length; ++i) { mysqlBind ~= MYSQL_BIND(); //import core.stdc.string: memset; //memset(mb, 0, MYSQL_BIND.sizeof); // might not be needed: D struct @@ -719,8 +633,7 @@ private struct Driver(Policy) } } - struct Statement - { + struct Statement { Connection* con; string sql; Allocator allocator; @@ -730,8 +643,7 @@ private struct Driver(Policy) Array!MYSQL_BIND mysqlBind; bool bindInit; - this(Connection* con_, string sql_) - { + this(Connection* con_, string sql_) { con = con_; sql = sql_; //allocator = &con.db.allocator; @@ -740,8 +652,7 @@ private struct Driver(Policy) throw new DatabaseException("stmt error"); } - ~this() - { + ~this() { foreach (b; inputBind) allocator.deallocate(b.data); if (stmt) @@ -750,28 +661,23 @@ private struct Driver(Policy) } // hoist? - this(this) - { + this(this) { assert(false); } - void opAssign(Statement rhs) - { + void opAssign(Statement rhs) { assert(false); } - void prepare() - { + void prepare() { check("mysql_stmt_prepare", stmt, mysql_stmt_prepare(stmt, cast(char*) sql.ptr, sql.length)); binds = cast(uint) mysql_stmt_param_count(stmt); } - void query() - { - if (inputBind.length && !bindInit) - { + void query() { + if (inputBind.length && !bindInit) { bindInit = true; bindSetup(inputBind, mysqlBind); @@ -784,30 +690,25 @@ private struct Driver(Policy) check("mysql_stmt_execute", stmt, mysql_stmt_execute(stmt)); } - void query(X...)(X args) - { + void query(X...)(X args) { bindAll(args); query(); } - bool hasRows() - { + bool hasRows() { return true; } - void reset() - { + void reset() { } - private void bindAll(T...)(T args) - { + private void bindAll(T...)(T args) { int col; foreach (arg; args) bind(++col, arg); } - void bind(int n, int value) - { + void bind(int n, int value) { info("input bind: n: ", n, ", value: ", value); auto b = bindAlloc(n, MYSQL_TYPE_LONG, int.sizeof); b.is_null = 0; @@ -816,8 +717,7 @@ private struct Driver(Policy) } - void bind(int n, const char[] value) - { + void bind(int n, const char[] value) { import core.stdc.string : strncpy; info("input bind: n: ", n, ", value: ", value); @@ -832,8 +732,7 @@ private struct Driver(Policy) b.length = value.length; } - void bind(int n, Date d) - { + void bind(int n, Date d) { auto b = bindAlloc(n, MYSQL_TYPE_DATE, MYSQL_TIME.sizeof); b.is_null = 0; b.error = 0; @@ -844,8 +743,7 @@ private struct Driver(Policy) p.day = d.day; } - Bind* bindAlloc(int n, int mysql_type, int allocSize) - { + Bind* bindAlloc(int n, int mysql_type, int allocSize) { if (n == 0) throw new DatabaseException("zero index"); auto idx = n - 1; @@ -864,8 +762,7 @@ private struct Driver(Policy) } - struct Result - { + struct Result { Statement* stmt; Allocator allocator; uint columns; @@ -877,8 +774,7 @@ private struct Driver(Policy) static const maxData = 256; - this(Statement* stmt_, int rowArraySize_) - { + this(Statement* stmt_, int rowArraySize_) { stmt = stmt_; //allocator = stmt.allocator; @@ -889,8 +785,7 @@ private struct Driver(Policy) build_bind(); } - ~this() - { + ~this() { //log("~Result"); foreach (b; bind) allocator.deallocate(b.data); @@ -898,16 +793,14 @@ private struct Driver(Policy) mysql_free_result(result_metadata); } - void build_describe() - { + void build_describe() { import core.stdc.string : strlen; columns = cast(uint) mysql_stmt_field_count(stmt.stmt); describe.reserve(columns); - for (int i = 0; i < columns; ++i) - { + for (int i = 0; i < columns; ++i) { describe ~= Describe(); auto d = &describe.back(); @@ -921,65 +814,62 @@ private struct Driver(Policy) } } - void build_bind() - { + void build_bind() { import core.stdc.string : memset; import core.memory : GC; bind.reserve(columns); - for (int i = 0; i != columns; ++i) - { - auto d = &describe[i]; - bind ~= Bind(); - auto b = &bind.back(); - b.mysql_type = d.field.type; - b.allocSize = cast(uint)(d.field.length + 1); - switch (d.field.type) - { - case MYSQL_TYPE_TINY: - b.type = ValueType.Char; - break; - case MYSQL_TYPE_SHORT: - b.type = ValueType.Short; - break; - case MYSQL_TYPE_LONG: - b.type = ValueType.Int; - break; - case MYSQL_TYPE_LONGLONG: - b.type = ValueType.Long; - break; - case MYSQL_TYPE_FLOAT: - b.type = ValueType.Float; - break; - case MYSQL_TYPE_DOUBLE: - b.type = ValueType.Double; - break; - - case MYSQL_TYPE_DATE: - b.type = ValueType.Date; - break; - case MYSQL_TYPE_TIME: - case MYSQL_TYPE_TIME2: - b.type = ValueType.Time; - b.allocSize += 30; - break; - case MYSQL_TYPE_DATETIME2: - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIMESTAMP2: - case MYSQL_TYPE_TIMESTAMP: - b.type = ValueType.DateTime; - b.allocSize += 30; - break; - default: - b.mysql_type = MYSQL_TYPE_STRING; - b.type = ValueType.String; - b.allocSize += 256; - break; - - } - // let in ints for now - /* if (d.field.type == MYSQL_TYPE_LONG) { + for (int i = 0; i != columns; ++i) { + auto d = &describe[i]; + bind ~= Bind(); + auto b = &bind.back(); + b.mysql_type = d.field.type; + b.allocSize = cast(uint)(d.field.length + 1); + switch (d.field.type) { + case MYSQL_TYPE_TINY: + b.type = ValueType.Char; + break; + case MYSQL_TYPE_SHORT: + b.type = ValueType.Short; + break; + case MYSQL_TYPE_LONG: + b.type = ValueType.Int; + break; + case MYSQL_TYPE_LONGLONG: + b.type = ValueType.Long; + break; + case MYSQL_TYPE_FLOAT: + b.type = ValueType.Float; + break; + case MYSQL_TYPE_DOUBLE: + b.type = ValueType.Double; + break; + + case MYSQL_TYPE_DATE: + b.type = ValueType.Date; + break; + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_TIME2: + b.type = ValueType.Time; + b.allocSize += 30; + break; + case MYSQL_TYPE_DATETIME2: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP2: + case MYSQL_TYPE_TIMESTAMP: + b.type = ValueType.DateTime; + b.allocSize += 30; + break; + default: + b.mysql_type = MYSQL_TYPE_STRING; + b.type = ValueType.String; + b.allocSize += 256; + break; + + } + // let in ints for now + /* if (d.field.type == MYSQL_TYPE_LONG) { b.mysql_type = d.field.type; b.type = ValueType.Int; } else if (d.field.type == MYSQL_TYPE_DATE) { @@ -989,28 +879,24 @@ private struct Driver(Policy) b.mysql_type = MYSQL_TYPE_STRING; b.type = ValueType.String; }*/ - b.data = allocator.allocate(b.allocSize); - } - - bindSetup(bind, mysqlBind); + b.data = allocator.allocate(b.allocSize); + } + + bindSetup(bind, mysqlBind); mysql_stmt_bind_result(stmt.stmt, &mysqlBind.front()); } - int fetch() - { + int fetch() { status = check("mysql_stmt_fetch", stmt.stmt, mysql_stmt_fetch(stmt.stmt)); - if (!status) - { + if (!status) { return 1; } - else if (status == MYSQL_NO_DATA) - { + else if (status == MYSQL_NO_DATA) { //rows_ = row_count_; return 0; } - else if (status == MYSQL_DATA_TRUNCATED) - { + else if (status == MYSQL_DATA_TRUNCATED) { raiseError("mysql_stmt_fetch: truncation", status); } @@ -1018,11 +904,9 @@ private struct Driver(Policy) return 0; } - Variant getValue(Cell* cell) - { + Variant getValue(Cell* cell) { Variant value; - switch (cell.bind.type) - { + switch (cell.bind.type) { case ValueType.Char: value = (*cast(char*) cell.bind.data.ptr); break; @@ -1041,26 +925,22 @@ private struct Driver(Policy) case ValueType.Double: value = (*cast(double*) cell.bind.data.ptr); break; - case ValueType.String: - { + case ValueType.String: { auto ptr = cast(char*) cell.bind.data.ptr; value = cast(string)(ptr[0 .. cell.bind.length]); } break; - case ValueType.Date: - { + case ValueType.Date: { MYSQL_TIME* t = cast(MYSQL_TIME*) cell.bind.data.ptr; value = Date(t.year, t.month, t.day); } break; - case ValueType.Time: - { + case ValueType.Time: { MYSQL_TIME* t = cast(MYSQL_TIME*) cell.bind.data.ptr; value = Time(t.hour, t.minute, t.second); } break; - case ValueType.DateTime: - { + case ValueType.DateTime: { MYSQL_TIME* t = cast(MYSQL_TIME*) cell.bind.data.ptr; value = DateTime(t.year, t.month, t.day, t.hour, t.minute, t.second); @@ -1074,24 +954,20 @@ private struct Driver(Policy) // value getters - auto name(size_t idx) - { + auto name(size_t idx) { return describe[idx].name; } - ubyte[] rawData(Cell* cell) - { + ubyte[] rawData(Cell* cell) { auto ptr = cast(ubyte*) cell.bind.data.ptr; return ptr[0 .. cell.bind.length]; } - bool isNull(Cell* cell) - { + bool isNull(Cell* cell) { return cell.bind.is_null > 0; } - static void checkType(T)(Bind* b) - { + static void checkType(T)(Bind* b) { int x = TypeInfo!T.type(); int y = b.mysql_type; if (x == y) @@ -1101,26 +977,20 @@ private struct Driver(Policy) } // refactor as a better 1-n bind mapping - struct TypeInfo(T : int) - { - static int type() - { + struct TypeInfo(T : int) { + static int type() { return MYSQL_TYPE_LONG; } } - struct TypeInfo(T : string) - { - static int type() - { + struct TypeInfo(T : string) { + static int type() { return MYSQL_TYPE_STRING; } } - struct TypeInfo(T : Date) - { - static int type() - { + struct TypeInfo(T : Date) { + static int type() { return MYSQL_TYPE_DATE; } } diff --git a/src/std/database/odbc/database.d b/src/std/database/odbc/database.d index 35ccca9..d4a7694 100644 --- a/src/std/database/odbc/database.d +++ b/src/std/database/odbc/database.d @@ -29,24 +29,22 @@ struct DefaultPolicy { alias Allocator = MyMallocator; } -alias Database(T) = BasicDatabase!(Driver!T,T); +alias Database(T) = BasicDatabase!(Driver!T, T); -auto createDatabase()(string defaultURI="") { - return Database!DefaultPolicy(defaultURI); +auto createDatabase()(string defaultURI = "") { + return Database!DefaultPolicy(defaultURI); } struct Driver(Policy) { alias Allocator = Policy.Allocator; - alias Cell = BasicCell!(Driver,Policy); + alias Cell = BasicCell!(Driver, Policy); struct Database { alias queryVariableType = QueryVariableType.QuestionMark; - static const FeatureArray features = [ - //Feature.InputBinding, - //Feature.DateBinding, - Feature.ConnectionPool, - ]; + static const FeatureArray features = [//Feature.InputBinding, + //Feature.DateBinding, + Feature.ConnectionPool,]; Allocator allocator; SQLHENV env; @@ -54,24 +52,22 @@ struct Driver(Policy) { this(string defaultURI_) { info("Database"); allocator = Allocator(); - check( - "SQLAllocHandle", - SQLAllocHandle( - SQL_HANDLE_ENV, - SQL_NULL_HANDLE, - &env)); - SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, cast(void *) SQL_OV_ODBC3, 0); + check("SQLAllocHandle", SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, + &env)); + SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, cast(void*) SQL_OV_ODBC3, 0); } ~this() { info("~Database"); - if (!env) return; - check("SQLFreeHandle", SQL_HANDLE_ENV, env, SQLFreeHandle(SQL_HANDLE_ENV, env)); + if (!env) + return; + check("SQLFreeHandle", SQL_HANDLE_ENV, env, SQLFreeHandle(SQL_HANDLE_ENV, + env)); env = null; } void showDrivers() { - import core.stdc.string: strlen; + import core.stdc.string : strlen; SQLUSMALLINT direction; @@ -83,17 +79,10 @@ struct Driver(Policy) { direction = SQL_FETCH_FIRST; info("DRIVERS:"); - while(SQL_SUCCEEDED(ret = SQLDrivers( - env, - direction, - driver.ptr, - driver.sizeof, - &driver_ret, - attr.ptr, - attr.sizeof, - &attr_ret))) { + while (SQL_SUCCEEDED(ret = SQLDrivers(env, direction, driver.ptr, + driver.sizeof, &driver_ret, attr.ptr, attr.sizeof, &attr_ret))) { direction = SQL_FETCH_NEXT; - info(driver.ptr[0..strlen(driver.ptr)], ": ", attr.ptr[0..strlen(attr.ptr)]); + info(driver.ptr[0 .. strlen(driver.ptr)], ": ", attr.ptr[0 .. strlen(attr.ptr)]); //if (ret == SQL_SUCCESS_WITH_INFO) printf("\tdata truncation\n"); } } @@ -115,25 +104,22 @@ struct Driver(Policy) { SQLSMALLINT outstrlen; //string DSN = "DSN=testdb"; - SQLRETURN ret = SQLAllocHandle(SQL_HANDLE_DBC,db.env,&con); + SQLRETURN ret = SQLAllocHandle(SQL_HANDLE_DBC, db.env, &con); if ((ret != SQL_SUCCESS) && (ret != SQL_SUCCESS_WITH_INFO)) { throw new DatabaseException("SQLAllocHandle error: " ~ to!string(ret)); } - check("SQLConnect", SQL_HANDLE_DBC, con, SQLConnect( - con, - cast(SQLCHAR*) toStringz(source.server), - SQL_NTS, - cast(SQLCHAR*) toStringz(source.username), - SQL_NTS, - cast(SQLCHAR*) toStringz(source.password), - SQL_NTS)); + check("SQLConnect", SQL_HANDLE_DBC, con, SQLConnect(con, + cast(SQLCHAR*) toStringz(source.server), SQL_NTS, + cast(SQLCHAR*) toStringz(source.username), SQL_NTS, + cast(SQLCHAR*) toStringz(source.password), SQL_NTS)); connected = true; } ~this() { info("~Connection: ", source); - if (connected) check("SQLDisconnect()", SQL_HANDLE_DBC, con, SQLDisconnect(con)); + if (connected) + check("SQLDisconnect()", SQL_HANDLE_DBC, con, SQLDisconnect(con)); check("SQLFreeHandle", SQLFreeHandle(SQL_HANDLE_DBC, con)); } } @@ -141,7 +127,7 @@ struct Driver(Policy) { struct Statement { Connection* con; string sql; - Allocator *allocator; + Allocator* allocator; SQLHSTMT stmt; bool hasRows_; // not working int binds; @@ -157,10 +143,11 @@ struct Driver(Policy) { ~this() { info("~Statement"); - for(int i = 0; i < inputbind_.length; ++i) { - allocator.deallocate(inputbind_[i].data[0..inputbind_[i].allocSize]); + for (int i = 0; i < inputbind_.length; ++i) { + allocator.deallocate(inputbind_[i].data[0 .. inputbind_[i].allocSize]); } - if (stmt) check("SQLFreeHandle", SQLFreeHandle(SQL_HANDLE_STMT, stmt)); + if (stmt) + check("SQLFreeHandle", SQLFreeHandle(SQL_HANDLE_STMT, stmt)); // stmt = null? needed } @@ -175,11 +162,12 @@ struct Driver(Policy) { b.data = cast(void*)(allocator.allocate(b.allocSize)); inputBind(n, b); - *(cast(SQLINTEGER*) inputbind_[n-1].data) = value; + *(cast(SQLINTEGER*) inputbind_[n - 1].data) = value; } - void bind(int n, const char[] value){ - import core.stdc.string: strncpy; + void bind(int n, const char[] value) { + import core.stdc.string : strncpy; + info("input bind: n: ", n, ", value: ", value); // no null termination needed @@ -201,44 +189,31 @@ struct Driver(Policy) { inputbind_ ~= bind; auto b = &inputbind_.back(); - check("SQLBindParameter", SQLBindParameter( - stmt, - cast(SQLSMALLINT) n, - SQL_PARAM_INPUT, - b.bindType, - b.dbtype, - 0, - 0, - b.data, - b.allocSize, - null)); + check("SQLBindParameter", SQLBindParameter(stmt, + cast(SQLSMALLINT) n, SQL_PARAM_INPUT, b.bindType, b.dbtype, 0, + 0, b.data, b.allocSize, null)); } - void prepare() { //if (!data_.st) - check("SQLPrepare", SQLPrepare( - stmt, - cast(SQLCHAR*) toStringz(sql), - SQL_NTS)); + check("SQLPrepare", SQLPrepare(stmt, cast(SQLCHAR*) toStringz(sql), SQL_NTS)); SQLSMALLINT v; check("SQLNumParams", SQLNumParams(stmt, &v)); binds = v; - check("SQLNumResultCols", SQLNumResultCols (stmt, &v)); + check("SQLNumResultCols", SQLNumResultCols(stmt, &v)); info("binds: ", binds); } void query() { if (!binds) { info("sql execute direct: ", sql); - SQLRETURN ret = SQLExecDirect( - stmt, - cast(SQLCHAR*) toStringz(sql), - SQL_NTS); + SQLRETURN ret = SQLExecDirect(stmt, cast(SQLCHAR*) toStringz(sql), + SQL_NTS); check("SQLExecuteDirect()", SQL_HANDLE_STMT, stmt, ret); hasRows_ = ret != SQL_NO_DATA; - } else { + } + else { info("sql execute prepared: ", sql); SQLRETURN ret = SQLExecute(stmt); check("SQLExecute()", SQL_HANDLE_STMT, stmt, ret); @@ -246,22 +221,25 @@ struct Driver(Policy) { } } - void query(X...) (X args) { + void query(X...)(X args) { bindAll(args); query(); } - bool hasRows() {return hasRows_;} + bool hasRows() { + return hasRows_; + } void exec() { - check("SQLExecDirect", SQLExecDirect(stmt,cast(SQLCHAR*) toStringz(sql), SQL_NTS)); + check("SQLExecDirect", SQLExecDirect(stmt, cast(SQLCHAR*) toStringz(sql), + SQL_NTS)); } void reset() { //SQLCloseCursor } - private void bindAll(T...) (T args) { + private void bindAll(T...)(T args) { int col; foreach (arg; args) { bind(++col, arg); @@ -269,13 +247,12 @@ struct Driver(Policy) { } } - struct Describe { static const nameSize = 256; char[nameSize] name; SQLSMALLINT nameLen; SQLSMALLINT type; - SQLULEN size; + SQLULEN size; SQLSMALLINT digits; SQLSMALLINT nullable; SQLCHAR* data; @@ -296,8 +273,8 @@ struct Driver(Policy) { //SQLULEN allocSize; //SQLLEN len; - SQLINTEGER size; - SQLINTEGER allocSize; + SQLINTEGER size; + SQLINTEGER allocSize; SQLINTEGER len; } @@ -307,7 +284,7 @@ struct Driver(Policy) { static const maxData = 256; Statement* stmt; - Allocator *allocator; + Allocator* allocator; int columns; Array!Describe describe; Array!Bind bind; @@ -320,14 +297,14 @@ struct Driver(Policy) { // check for result set (probably a better way) //if (!stmt.hasRows) return; SQLSMALLINT v; - check("SQLNumResultCols", SQLNumResultCols (stmt.stmt, &v)); + check("SQLNumResultCols", SQLNumResultCols(stmt.stmt, &v)); columns = v; build_describe(); build_bind(); } ~this() { - for(int i = 0; i < bind.length; ++i) { + for (int i = 0; i < bind.length; ++i) { free(bind[i].data); } } @@ -336,20 +313,14 @@ struct Driver(Policy) { describe.reserve(columns); - for(int i = 0; i < columns; ++i) { + for (int i = 0; i < columns; ++i) { describe ~= Describe(); auto d = &describe.back(); - check("SQLDescribeCol", SQLDescribeCol( - stmt.stmt, - cast(SQLUSMALLINT) (i+1), - cast(SQLCHAR *) d.name, - cast(SQLSMALLINT) Describe.nameSize, - &d.nameLen, - &d.type, - &d.size, - &d.digits, - &d.nullable)); + check("SQLDescribeCol", SQLDescribeCol(stmt.stmt, + cast(SQLUSMALLINT)(i + 1), cast(SQLCHAR*) d.name, + cast(SQLSMALLINT) Describe.nameSize, &d.nameLen, &d.type, + &d.size, &d.digits, &d.nullable)); //info("NAME: ", d.name, ", type: ", d.type); } @@ -360,44 +331,36 @@ struct Driver(Policy) { bind.reserve(columns); - for(int i = 0; i < columns; ++i) { + for (int i = 0; i < columns; ++i) { bind ~= Bind(); auto b = &bind.back(); auto d = &describe[i]; b.size = d.size; - b.allocSize = cast(SQLULEN) (b.size + 1); + b.allocSize = cast(SQLULEN)(b.size + 1); b.data = cast(void*)(allocator.allocate(b.allocSize)); GC.addRange(b.data, b.allocSize); // just INT and VARCHAR for now switch (d.type) { - case SQL_INTEGER: - //b.type = SQL_C_LONG; - b.type = ValueType.String; - b.bindType = SQL_C_CHAR; - break; - case SQL_VARCHAR: - b.type = ValueType.String; - b.bindType = SQL_C_CHAR; - break; - default: - throw new DatabaseException("bind error: type: " ~ to!string(d.type)); + case SQL_INTEGER: + //b.type = SQL_C_LONG; + b.type = ValueType.String; + b.bindType = SQL_C_CHAR; + break; + case SQL_VARCHAR: + b.type = ValueType.String; + b.bindType = SQL_C_CHAR; + break; + default: + throw new DatabaseException("bind error: type: " ~ to!string(d.type)); } - check("SQLBINDCol", SQLBindCol ( - stmt.stmt, - cast(SQLUSMALLINT) (i+1), - b.bindType, - b.data, - b.size, - &b.len)); - - info( - "output bind: index: ", i, - ", type: ", b.bindType, - ", size: ", b.size, - ", allocSize: ", b.allocSize); + check("SQLBINDCol", SQLBindCol(stmt.stmt, + cast(SQLUSMALLINT)(i + 1), b.bindType, b.data, b.size, &b.len)); + + info("output bind: index: ", i, ", type: ", b.bindType, + ", size: ", b.size, ", allocSize: ", b.allocSize); } } @@ -405,8 +368,9 @@ struct Driver(Policy) { //info("SQLFetch"); status = SQLFetch(stmt.stmt); if (status == SQL_SUCCESS) { - return 1; - } else if (status == SQL_NO_DATA) { + return 1; + } + else if (status == SQL_NO_DATA) { stmt.reset(); return 0; } @@ -416,43 +380,45 @@ struct Driver(Policy) { auto name(size_t idx) { auto d = &describe[idx]; - return cast(string) d.name[0..d.nameLen]; + return cast(string) d.name[0 .. d.nameLen]; } - ubyte[] rawData(Cell* cell) { - auto ptr = cast(ubyte*) cell.bind.data; - return ptr[0..cell.bind.len]; - } - - Variant getValue(Cell* cell) - { - Variant = value; - if(cell.bind.type = ValueType.String) - { - auto ptr = cast(immutable char*) cell.bind.data; - value = cast(string) ptr[0..cell.bind.len]; - } - return value; //TODO: - } + ubyte[] rawData(Cell* cell) { + auto ptr = cast(ubyte*) cell.bind.data; + return ptr[0 .. cell.bind.len]; + } + Variant getValue(Cell* cell) { + Variant = value; + if (cell.bind.type = ValueType.String) { + auto ptr = cast(immutable char*) cell.bind.data; + value = cast(string) ptr[0 .. cell.bind.len]; + } + return value; //TODO: + } - bool isNull(Cell* cell){return false;} + bool isNull(Cell* cell) { + return false; + } void checkType(SQLSMALLINT a, SQLSMALLINT b) { - if (a != b) throw new DatabaseException("type mismatch"); + if (a != b) + throw new DatabaseException("type mismatch"); } } static void check()(string msg, SQLRETURN ret) { info(msg, ":", ret); - if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO || ret == SQL_NO_DATA) return; + if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO || ret == SQL_NO_DATA) + return; throw new DatabaseException("odbc error: " ~ msg); } static void check()(string msg, SQLSMALLINT handle_type, SQLHANDLE handle, SQLRETURN ret) { info(msg, ":", ret); - if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO || ret == SQL_NO_DATA) return; + if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO || ret == SQL_NO_DATA) + return; throw_detail(handle, handle_type, msg); } @@ -469,23 +435,17 @@ struct Driver(Policy) { error ~= ": "; do { - ret = SQLGetDiagRec( - type, - handle, - ++i, - cast(char*) state, - &native, - cast(char*)text, - text.length, - &len); + ret = SQLGetDiagRec(type, handle, ++i, cast(char*) state, &native, + cast(char*) text, text.length, &len); if (SQL_SUCCEEDED(ret)) { - auto s = text[0..len]; + auto s = text[0 .. len]; info("error: ", s); error ~= s; //writefln("%s:%ld:%ld:%s\n", state, i, native, text); } - } while (ret == SQL_SUCCESS); + } + while (ret == SQL_SUCCESS); throw new DatabaseException(error); } diff --git a/src/std/database/oracle/database.d b/src/std/database/oracle/database.d index a30f81a..e71522a 100644 --- a/src/std/database/oracle/database.d +++ b/src/std/database/oracle/database.d @@ -22,19 +22,20 @@ struct DefaultPolicy { alias Allocator = MyMallocator; } -alias Database(T) = BasicDatabase!(Driver!T,T); +alias Database(T) = BasicDatabase!(Driver!T, T); -auto createDatabase()(string defaultURI="") { - return Database!DefaultPolicy(defaultURI); +auto createDatabase()(string defaultURI = "") { + return Database!DefaultPolicy(defaultURI); } -auto createDatabase(T)(string defaultURI="") { - return Database!T(defaultURI); +auto createDatabase(T)(string defaultURI = "") { + return Database!T(defaultURI); } void check()(string msg, sword status) { info(msg, ":", status); - if (status == OCI_SUCCESS) return; + if (status == OCI_SUCCESS) + return; throw new DatabaseException("OCI error: " ~ msg); } @@ -42,51 +43,35 @@ void check()(string msg, sword status) { //static auto create()(string uri="") {return Database!DefaultPolicy();} //} - -T attrGet(T)(OCIStmt *stmt, OCIError *error, ub4 attribute) { +T attrGet(T)(OCIStmt* stmt, OCIError* error, ub4 attribute) { T value; ub4 sz = T.sizeof; attrGet!T(stmt, error, attribute, value); return value; } -void attrGet(T)(OCIStmt *stmt, OCIError *error, ub4 attribute, ref T value) { +void attrGet(T)(OCIStmt* stmt, OCIError* error, ub4 attribute, ref T value) { return attrGet(stmt, OCI_HTYPE_STMT, error, attribute, value); } void attrGet(T)(void* handle, ub4 handleType, OCIError* error, ub4 attribute, ref T value) { ub4 sz = T.sizeof; - check("OCIAttrGet", OCIAttrGet( - handle, - OCI_HTYPE_STMT, - &value, - &sz, - attribute, - error)); + check("OCIAttrGet", OCIAttrGet(handle, OCI_HTYPE_STMT, &value, &sz, attribute, + error)); } - - - struct Driver(Policy) { alias Allocator = Policy.Allocator; - alias Cell = BasicCell!(Driver,Policy); - - + alias Cell = BasicCell!(Driver, Policy); - static void attrSet(T)(OCIStmt *stmt, ub4 attribute, ref T value) { + static void attrSet(T)(OCIStmt* stmt, ub4 attribute, ref T value) { return attrSet(stmt, OCI_HTYPE_STMT, attribute, value); } static void attrSet(T)(void* handle, ub4 handleType, ub4 attribute, T value) { ub4 sz = T.sizeof; - check("OCIAttrSet", OCIAttrSet( - stmt, - OCI_HTYPE_STMT, - &value, - sz, - attribute, - error)); + check("OCIAttrSet", OCIAttrSet(stmt, OCI_HTYPE_STMT, &value, sz, attribute, + error)); } struct BindContext { @@ -96,7 +81,7 @@ struct Driver(Policy) { int rowArraySize; } - static void basicCtor(T) (ref BindContext ctx) { + static void basicCtor(T)(ref BindContext ctx) { emplace(cast(T*) ctx.bind.data); } @@ -122,275 +107,236 @@ struct Driver(Policy) { struct Conversion { int colType, bindType; - private static const Conversion[4] info = [ - {SQLT_INT,SQLT_INT}, - {SQLT_NUM,SQLT_STR}, - {SQLT_CHR,SQLT_STR}, - {SQLT_DAT,SQLT_DAT} - ]; - - static int get(int colType) { - // needs improvement - foreach(ref i; info) { - if (i.colType == colType) return i.bindType; - } - throw new DatabaseException( - "unknown conversion for column: " ~ to!string(colType)); - } - } + private static const Conversion[4] info = [{SQLT_INT, SQLT_INT}, { + SQLT_NUM, SQLT_STR}, {SQLT_CHR, SQLT_STR}, {SQLT_DAT, SQLT_DAT}]; - struct BindInfo { - ub2 bindType; - void function(ref BindContext ctx) binder; - - static const BindInfo[4] info = [ - {SQLT_INT,&charBinder}, - {SQLT_STR,&charBinder}, - {SQLT_STR,&charBinder}, - {SQLT_DAT,&dateBinder!(SQLT_ODT,OCIDate)} - ]; - - static void bind(ref BindContext ctx) { - import core.memory : GC; // needed? - auto d = ctx.describe, b = ctx.bind; - auto colType = ctx.describe.oType; - auto bindType = Conversion.get(colType); - auto allocator = ctx.allocator; - - // needs improvement - foreach(ref i; info) { - if (i.bindType == bindType) { - i.binder(ctx); - b.data = allocator.allocate(b.allocSize * ctx.rowArraySize); - b.ind = allocator.allocate(sb2.sizeof * ctx.rowArraySize); - b.length = allocator.allocate(ub2.sizeof * ctx.rowArraySize); - if (b.ctor) b.ctor(ctx); - GC.addRange(b.data.ptr, b.data.length); - return; + static int get(int colType) { + // needs improvement + foreach (ref i; info) { + if (i.colType == colType) + return i.bindType; } + throw new DatabaseException("unknown conversion for column: " ~ to!string(colType)); } - - throw new DatabaseException( - "bind not supported: " ~ to!string(colType)); - } - - } - - - struct Database { - alias queryVariableType = QueryVariableType.Dollar; - - Allocator allocator; - OCIEnv* env; - OCIError *error; - - static const FeatureArray features = [ - //Feature.InputBinding, - //Feature.DateBinding, - //Feature.ConnectionPool, - Feature.OutputArrayBinding, - ]; - - this(string defaultURI_) { - allocator = Allocator(); - info("oracle: opening database"); - ub4 mode = OCI_THREADED | OCI_OBJECT; - - check("OCIEnvCreate", OCIEnvCreate( - &env, - mode, - null, - null, - null, - null, - 0, - null)); - - check("OCIHandleAlloc", OCIHandleAlloc( - env, - cast(void**) &error, - OCI_HTYPE_ERROR, - 0, - null)); - } - - ~this() { - info("oracle: closing database"); - if (env) { - sword status = OCIHandleFree(env, OCI_HTYPE_ENV); - env = null; - } - } - - } - - struct Connection { - Database* db; - Source source; - OCIError *error; - OCISvcCtx *svc_ctx; - bool connected; - - this(Database* db_, Source source_) {hhkk: - db = db_; - source = source_; - error = db.error; - - string dbname = getDBName(source); - - check("OCILogon: ", OCILogon( - db_.env, - error, - &svc_ctx, - cast(const OraText*) source.username.ptr, - cast(ub4) source.username.length, - cast(const OraText*) source.password.ptr, - cast(ub4) source.password.length, - cast(const OraText*) dbname.ptr, - cast(ub4) dbname.length)); - - connected = true; } - ~this() { - if (svc_ctx) check("OCILogoff", OCILogoff(svc_ctx, error)); - } - - static string getDBName(Source src) { - string dbName = - "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)" ~ - "(HOST=" ~ src.server ~ ")" ~ - "(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=XE)))"; // need service name - return dbName; - } - } - - static const nameSize = 256; - - struct Describe { - //char[nameSize] name; - - OraText* ora_name; - string name; - int index; - ub4 name_len; - //string name; - ub2 oType; - ub2 size; - //const nd_oracle_type_info *type_info; - //oracle_bind_type bind_type; - OCIParam *param; - OCIDefine *define; - } + struct BindInfo { + ub2 bindType; + void function(ref BindContext ctx) binder; + + static const BindInfo[4] info = [{SQLT_INT, &charBinder}, { + SQLT_STR, &charBinder}, {SQLT_STR, &charBinder}, { + SQLT_DAT, &dateBinder!(SQLT_ODT, OCIDate)}]; + + static void bind(ref BindContext ctx) { + import core.memory : GC; // needed? + auto d = ctx.describe, b = ctx.bind; + auto colType = ctx.describe.oType; + auto bindType = Conversion.get(colType); + auto allocator = ctx.allocator; + + // needs improvement + foreach (ref i; info) { + if (i.bindType == bindType) { + i.binder(ctx); + b.data = allocator.allocate(b.allocSize * ctx.rowArraySize); + b.ind = allocator.allocate(sb2.sizeof * ctx.rowArraySize); + b.length = allocator.allocate(ub2.sizeof * ctx.rowArraySize); + if (b.ctor) + b.ctor(ctx); + GC.addRange(b.data.ptr, b.data.length); + return; + } + } + + throw new DatabaseException("bind not supported: " ~ to!string(colType)); + } - struct Bind { - ValueType type; - void[] data; - sb4 allocSize; - ub2 oType; - void[] ind; - void[] length; - void function(ref BindContext ctx) ctor; - void function(void[] data) dtor; - } - - // non memeber needed to avoid forward error - //auto range(T)(Statement!T stmt) {return Result!T(stmt).range();} - - struct Statement { - Connection *con; - string sql; - Allocator *allocator; - OCIError *error; - OCIStmt *stmt; - ub2 stmt_type; - int binds; - Array!Bind inputBind; - - this(Connection* con_, string sql_) { - con = con_; - sql = sql_; - allocator = &con.db.allocator; - error = con.error; - - check("OCIHandleAlloc", OCIHandleAlloc( - con.db.env, - cast(void**) &stmt, - OCI_HTYPE_STMT, - 0, - null)); - - //attrSet!ub4(OCI_ATTR_PREFETCH_ROWS, 1000); - } + } - ~this() { - for(int i = 0; i < inputBind.length; ++i) { - allocator.deallocate(inputBind[i].data[0..inputBind[i].allocSize]); - } - if (stmt) check("OCIHandleFree", OCIHandleFree(stmt,OCI_HTYPE_STMT)); - // stmt = null? needed - } + struct Database { + alias queryVariableType = QueryVariableType.Dollar; - void exec() { - //check("SQLExecDirect", SQLExecDirect(data_.stmt,cast(SQLCHAR*) toStringz(data_.sql), SQL_NTS)); - } + Allocator allocator; + OCIEnv* env; + OCIError* error; - void prepare() { + static const FeatureArray features = [//Feature.InputBinding, + //Feature.DateBinding, + //Feature.ConnectionPool, + Feature.OutputArrayBinding,]; - check("OCIStmtPrepare", OCIStmtPrepare( - stmt, - error, - cast(OraText*) sql.ptr, - cast(ub4) sql.length, - OCI_NTV_SYNTAX, - OCI_DEFAULT)); + this(string defaultURI_) { + allocator = Allocator(); + info("oracle: opening database"); + ub4 mode = OCI_THREADED | OCI_OBJECT; + check("OCIEnvCreate", OCIEnvCreate(&env, mode, null, + null, null, null, 0, null)); - // get the type of statement - stmt_type = attrGet!ub2(OCI_ATTR_STMT_TYPE); - binds = attrGet!ub4(OCI_ATTR_BIND_COUNT); - info("binds: ", binds); - } + check("OCIHandleAlloc", OCIHandleAlloc(env, + cast(void**)&error, OCI_HTYPE_ERROR, 0, null)); + } - void query() { - ub4 iters = stmt_type == OCI_STMT_SELECT ? 0:1; - info("iters: ", iters); - info("execute sql: ", sql); - - check("OCIStmtExecute", OCIStmtExecute( - con.svc_ctx, - stmt, - error, - iters, - 0, - null, - null, - OCI_COMMIT_ON_SUCCESS)); - } + ~this() { + info("oracle: closing database"); + if (env) { + sword status = OCIHandleFree(env, OCI_HTYPE_ENV); + env = null; + } + } - void query(X...) (X args) { - foreach (arg; args) { - info("ARG: ", arg); - } + } - info("variadic query not implemented yet"); - //bindAll(args); - //query(); - } + struct Connection { + Database* db; + Source source; + OCIError* error; + OCISvcCtx* svc_ctx; + bool connected; + + this(Database* db_, Source source_) { + hhkk: + db = db_; + source = source_; + error = db.error; + + string dbname = getDBName(source); + + check("OCILogon: ", OCILogon(db_.env, error, &svc_ctx, + cast(const OraText*) source.username.ptr, + cast(ub4) source.username.length, + cast(const OraText*) source.password.ptr, + cast(ub4) source.password.length, + cast(const OraText*) dbname.ptr, cast(ub4) dbname.length)); + + connected = true; + } + + ~this() { + if (svc_ctx) + check("OCILogoff", OCILogoff(svc_ctx, error)); + } + + static string getDBName(Source src) { + string dbName = "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)" ~ "(HOST=" ~ src.server + ~ ")" ~ "(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=XE)))"; // need service name + return dbName; + } + } - bool hasRows() { - // need a better way - int columns = attrGet!ub4(OCI_ATTR_PARAM_COUNT); - return columns != 0; - } + static const nameSize = 256; + + struct Describe { + //char[nameSize] name; + + OraText* ora_name; + string name; + int index; + ub4 name_len; + //string name; + ub2 oType; + ub2 size; + //const nd_oracle_type_info *type_info; + //oracle_bind_type bind_type; + OCIParam* param; + OCIDefine* define; + } - private void bindAll(T...) (T args) { - int col; - foreach (arg; args) bind(++col, arg); - } + struct Bind { + ValueType type; + void[] data; + sb4 allocSize; + ub2 oType; + void[] ind; + void[] length; + void function(ref BindContext ctx) ctor; + void function(void[] data) dtor; + } - void bind(int n, int value) { - /* + // non memeber needed to avoid forward error + //auto range(T)(Statement!T stmt) {return Result!T(stmt).range();} + + struct Statement { + Connection* con; + string sql; + Allocator* allocator; + OCIError* error; + OCIStmt* stmt; + ub2 stmt_type; + int binds; + Array!Bind inputBind; + + this(Connection* con_, string sql_) { + con = con_; + sql = sql_; + allocator = &con.db.allocator; + error = con.error; + + check("OCIHandleAlloc", OCIHandleAlloc(con.db.env, + cast(void**)&stmt, OCI_HTYPE_STMT, 0, null)); + + //attrSet!ub4(OCI_ATTR_PREFETCH_ROWS, 1000); + } + + ~this() { + for (int i = 0; i < inputBind.length; ++i) { + allocator.deallocate(inputBind[i].data[0 .. inputBind[i].allocSize]); + } + if (stmt) + check("OCIHandleFree", OCIHandleFree(stmt, OCI_HTYPE_STMT)); + // stmt = null? needed + } + + void exec() { + //check("SQLExecDirect", SQLExecDirect(data_.stmt,cast(SQLCHAR*) toStringz(data_.sql), SQL_NTS)); + } + + void prepare() { + + check("OCIStmtPrepare", OCIStmtPrepare(stmt, error, + cast(OraText*) sql.ptr, cast(ub4) sql.length, + OCI_NTV_SYNTAX, OCI_DEFAULT)); + + // get the type of statement + stmt_type = attrGet!ub2(OCI_ATTR_STMT_TYPE); + binds = attrGet!ub4(OCI_ATTR_BIND_COUNT); + info("binds: ", binds); + } + + void query() { + ub4 iters = stmt_type == OCI_STMT_SELECT ? 0 : 1; + info("iters: ", iters); + info("execute sql: ", sql); + + check("OCIStmtExecute", OCIStmtExecute(con.svc_ctx, + stmt, error, iters, 0, null, null, OCI_COMMIT_ON_SUCCESS)); + } + + void query(X...)(X args) { + foreach (arg; args) { + info("ARG: ", arg); + } + + info("variadic query not implemented yet"); + //bindAll(args); + //query(); + } + + bool hasRows() { + // need a better way + int columns = attrGet!ub4(OCI_ATTR_PARAM_COUNT); + return columns != 0; + } + + private void bindAll(T...)(T args) { + int col; + foreach (arg; args) + bind(++col, arg); + } + + void bind(int n, int value) { + /* info("input bind: n: ", n, ", value: ", value); Bind b; @@ -403,10 +349,10 @@ struct Driver(Policy) { *(cast(SQLINTEGER*) data_.inputBind[n-1].data) = value; */ - } + } - void bind(int n, const char[] value){ - /* + void bind(int n, const char[] value) { + /* import core.stdc.string: strncpy; info("input bind: n: ", n, ", value: ", value); // no null termination needed @@ -421,259 +367,229 @@ struct Driver(Policy) { strncpy(cast(char*) b.data, value.ptr, b.size); */ - } - - void bind(int n, Date d) { - throw new DatabaseException("Date input binding not yet implemented"); - } - - void reset() {} - //: Error: template std.database.oracle.database.Driver!(DefaultPolicy).Driver.Statement.attrGet cannot deduce function from argument types !(ushort)(OCIStmt*, uint), candidates are: - - private T attrGet(T)(ub4 attribute) {return attrGet!T(stmt, error, attribute);} - private void attrGet(T)(ub4 attribute, ref T value) { return attrGet(stmt, error, attribute, value);} - private void attrSet(T)(ub4 attribute, T value) {return attrSet(stmt, error, attribute, value);} - - static T attrGet(T)(OCIStmt *stmt, OCIError *error, ub4 attribute) { - T value; - ub4 sz = T.sizeof; - attrGet!T(stmt, error, attribute, value); - return value; - } - - static void attrGet(T)(OCIStmt *stmt, OCIError *error, ub4 attribute, ref T value) { - return attrGet(stmt, OCI_HTYPE_STMT, error, attribute, value); - } + } + + void bind(int n, Date d) { + throw new DatabaseException("Date input binding not yet implemented"); + } + + void reset() { + } + //: Error: template std.database.oracle.database.Driver!(DefaultPolicy).Driver.Statement.attrGet cannot deduce function from argument types !(ushort)(OCIStmt*, uint), candidates are: + + private T attrGet(T)(ub4 attribute) { + return attrGet!T(stmt, error, attribute); + } + + private void attrGet(T)(ub4 attribute, ref T value) { + return attrGet(stmt, error, attribute, value); + } + + private void attrSet(T)(ub4 attribute, T value) { + return attrSet(stmt, error, attribute, value); + } + + static T attrGet(T)(OCIStmt* stmt, OCIError* error, ub4 attribute) { + T value; + ub4 sz = T.sizeof; + attrGet!T(stmt, error, attribute, value); + return value; + } + + static void attrGet(T)(OCIStmt* stmt, OCIError* error, + ub4 attribute, ref T value) { + return attrGet(stmt, OCI_HTYPE_STMT, error, attribute, value); + } + + static void attrGet(T)(void* handle, ub4 handleType, + OCIError* error, ub4 attribute, ref T value) { + ub4 sz = T.sizeof; + check("OCIAttrGet", OCIAttrGet(handle, OCI_HTYPE_STMT, + &value, &sz, attribute, error)); + } - static void attrGet(T)(void* handle, ub4 handleType, OCIError* error, ub4 attribute, ref T value) { - ub4 sz = T.sizeof; - check("OCIAttrGet", OCIAttrGet( - handle, - OCI_HTYPE_STMT, - &value, - &sz, - attribute, - error)); - } - - } - - struct Result { - Statement *stmt; - Allocator *allocator; - OCIError *error; - int columns; - Array!Describe describe; - Array!Bind bind; - ub4 rowArraySize; - sword status; - - this(Statement* stmt_, int rowArraySize_) { - stmt = stmt_; - rowArraySize = rowArraySize_; - allocator = &stmt.con.db.allocator; - error = stmt.error; - columns = stmt_.attrGet!ub4(OCI_ATTR_PARAM_COUNT); - build_describe(); - build_bind(); - } - - ~this() { - foreach(ref b; bind) { - if (b.dtor) b.dtor(b.data); - allocator.deallocate(b.data); - allocator.deallocate(b.ind); - allocator.deallocate(b.length); - } - } + } - void build_describe() { - - describe.reserve(columns); - for(int i = 0; i < columns; ++i) { - describe ~= Describe(); - auto d = &describe.back(); - - OCIParam *col; - check("OCIParamGet",OCIParamGet( - stmt.stmt, - OCI_HTYPE_STMT, - error, - cast(void**) &col, - i+1)); - - check("OCIAttrGet", OCIAttrGet( - col, - OCI_DTYPE_PARAM, - &d.ora_name, - &d.name_len, - OCI_ATTR_NAME, - error)); - - check("OCIAttrGet", OCIAttrGet( - col, - OCI_DTYPE_PARAM, - &d.oType, - null, - OCI_ATTR_DATA_TYPE, - error)); - - check("OCIAttrGet", OCIAttrGet( - col, - OCI_DTYPE_PARAM, - &d.size, - null, - OCI_ATTR_DATA_SIZE, - error)); - - d.name = to!string(cast(char*)(d.ora_name)[0..d.name_len]); - info("describe: name: ", d.name, ", type: ", d.oType, ", size: ", d.size); - } + struct Result { + Statement* stmt; + Allocator* allocator; + OCIError* error; + int columns; + Array!Describe describe; + Array!Bind bind; + ub4 rowArraySize; + sword status; + + this(Statement* stmt_, int rowArraySize_) { + stmt = stmt_; + rowArraySize = rowArraySize_; + allocator = &stmt.con.db.allocator; + error = stmt.error; + columns = stmt_.attrGet!ub4(OCI_ATTR_PARAM_COUNT); + build_describe(); + build_bind(); + } + + ~this() { + foreach (ref b; bind) { + if (b.dtor) + b.dtor(b.data); + allocator.deallocate(b.data); + allocator.deallocate(b.ind); + allocator.deallocate(b.length); + } + } + + void build_describe() { + + describe.reserve(columns); + for (int i = 0; i < columns; ++i) { + describe ~= Describe(); + auto d = &describe.back(); + + OCIParam* col; + check("OCIParamGet", OCIParamGet(stmt.stmt, + OCI_HTYPE_STMT, error, cast(void**)&col, i + 1)); + + check("OCIAttrGet", OCIAttrGet(col, + OCI_DTYPE_PARAM, &d.ora_name, &d.name_len, OCI_ATTR_NAME, + error)); + + check("OCIAttrGet", OCIAttrGet(col, + OCI_DTYPE_PARAM, &d.oType, null, OCI_ATTR_DATA_TYPE, + error)); + + check("OCIAttrGet", OCIAttrGet(col, + OCI_DTYPE_PARAM, &d.size, null, OCI_ATTR_DATA_SIZE, + error)); + + d.name = to!string(cast(char*)(d.ora_name)[0 .. d.name_len]); + info("describe: name: ", d.name, ", type: ", + d.oType, ", size: ", d.size); + } + + } + + void build_bind() { + auto allocator = Allocator(); + + import core.memory : GC; // needed? + + bind.reserve(columns); + + for (int i = 0; i < columns; ++i) { + bind ~= Bind(); + auto d = &describe[i]; + auto b = &bind.back(); + + BindContext ctx; + ctx.describe = d; + ctx.bind = b; + ctx.allocator = &allocator; + ctx.rowArraySize = rowArraySize; + + BindInfo.bind(ctx); + + info("bind: type: ", b.oType, ", allocSize:", b.allocSize); + + check("OCIDefineByPos", OCIDefineByPos(stmt.stmt, + &d.define, error, cast(ub4) i + 1, b.data.ptr, + b.allocSize, b.oType, cast(dvoid*) b.ind.ptr, // check + cast(ub2*) b.length.ptr, null, OCI_DEFAULT)); + + if (rowArraySize > 1) { + check("OCIDefineArrayOfStruct", + OCIDefineArrayOfStruct(d.define, error, + b.allocSize, sb2.sizeof, ub2.sizeof, 0)); + } + } + } + + int fetch() { + if (status == OCI_NO_DATA) + return 0; + + int rowsFetched; + + //info("OCIStmtFetch2"); + + status = OCIStmtFetch2(stmt.stmt, error, rowArraySize, + OCI_FETCH_NEXT, 0, OCI_DEFAULT); + + if (rowArraySize > 1) { + // clean up + ub4 value; + check("OCIAttrGet", OCIAttrGet(stmt.stmt, + OCI_HTYPE_STMT, &value, null, OCI_ATTR_ROWS_FETCHED, + error)); + rowsFetched = value; + } + else { + rowsFetched = (status == OCI_NO_DATA ? 0 : 1); + } + + if (status == OCI_SUCCESS) { + return rowsFetched; + } + else if (status == OCI_NO_DATA) { + //stmt.reset(); + return rowsFetched; + } + + throw new DatabaseException("fetch error"); // fix + //check("SQLFetch", SQL_HANDLE_STMT, stmt.data_.stmt, status); + //return 0; + } + + ubyte[] rawData(Cell* cell) { //TODO: fix + return null; + //auto ptr = cast(ubyte*) data(cell); + //return ptr[0..strlen(ptr)]; + } + + Variant getValue(Cell* cell) { + Variant value; + switch (cell.bind.oType) { + case SQLT_STR: { + import core.stdc.string : strlen; + + //checkType(cell.bind.oType, SQLT_STR); + auto ptr = cast(immutable char*) data(cell); + value = cast(string) ptr[0 .. strlen(ptr)]; // fix with length + } + break; + case SQLT_INT: + value = *(cast(int*) data(cell)); + break; + case SQLT_ODT: { + auto d = cast(OCIDate*) data(cell); + value = Date(d.OCIDateYYYY, d.OCIDateMM, d.OCIDateDD); + } + break; + default: + break; + + } + return value; //TODO: + } + + bool isNull(Cell* cell) { + return false; + } + + auto name(size_t idx) { + return describe[idx].name; + } + + private void* data(Cell* cell) { + return cell.bind.data.ptr + cell.bind.allocSize * cell.rowIdx; + } + + private static void checkType(ub2 a, ub2 b) { + if (a != b) + throw new DatabaseException("type mismatch"); + } - } + } - void build_bind() { - auto allocator = Allocator(); - - import core.memory : GC; // needed? - - bind.reserve(columns); - - for(int i = 0; i < columns; ++i) { - bind ~= Bind(); - auto d = &describe[i]; - auto b = &bind.back(); - - BindContext ctx; - ctx.describe = d; - ctx.bind = b; - ctx.allocator = &allocator; - ctx.rowArraySize = rowArraySize; - - BindInfo.bind(ctx); - - info("bind: type: ", b.oType, ", allocSize:", b.allocSize); - - check("OCIDefineByPos", OCIDefineByPos( - stmt.stmt, - &d.define, - error, - cast(ub4) i+1, - b.data.ptr, - b.allocSize, - b.oType, - cast(dvoid *) b.ind.ptr, // check - cast(ub2*) b.length.ptr, - null, - OCI_DEFAULT)); - - if (rowArraySize > 1) { - check("OCIDefineArrayOfStruct", OCIDefineArrayOfStruct( - d.define, - error, - b.allocSize, - sb2.sizeof, - ub2.sizeof, - 0)); } - } - } - - int fetch() { - if (status == OCI_NO_DATA) return 0; - - int rowsFetched; - - //info("OCIStmtFetch2"); - - status = OCIStmtFetch2( - stmt.stmt, - error, - rowArraySize, - OCI_FETCH_NEXT, - 0, - OCI_DEFAULT); - - if (rowArraySize > 1) { - // clean up - ub4 value; - check("OCIAttrGet",OCIAttrGet( - stmt.stmt, - OCI_HTYPE_STMT, - &value, - null, - OCI_ATTR_ROWS_FETCHED, - error)); - rowsFetched = value; - } else { - rowsFetched = (status == OCI_NO_DATA ? 0 : 1); - } - - if (status == OCI_SUCCESS) { - return rowsFetched; - } else if (status == OCI_NO_DATA) { - //stmt.reset(); - return rowsFetched; - } - - throw new DatabaseException("fetch error"); // fix - //check("SQLFetch", SQL_HANDLE_STMT, stmt.data_.stmt, status); - //return 0; - } - - ubyte[] rawData(Cell* cell) { //TODO: fix - return null; - //auto ptr = cast(ubyte*) data(cell); - //return ptr[0..strlen(ptr)]; - } - - Variant getValue(Cell* cell) - { - Variant value; - switch(cell.bind.oType) - { - case SQLT_STR: - { - import core.stdc.string: strlen; - //checkType(cell.bind.oType, SQLT_STR); - auto ptr = cast(immutable char*) data(cell); - value = cast(string) ptr[0..strlen(ptr)]; // fix with length - } - break; - case SQLT_INT: - value = *(cast(int*) data(cell)); - break; - case SQLT_ODT: - { - auto d = cast(OCIDate*) data(cell); - value = Date(d.OCIDateYYYY,d.OCIDateMM,d.OCIDateDD); - } - break; - default: - break; - - } - return value; //TODO: - } - - - bool isNull(Cell* cell){return false;} - - - auto name(size_t idx) { - return describe[idx].name; - } - - private void* data(Cell* cell) { - return cell.bind.data.ptr + cell.bind.allocSize * cell.rowIdx; - } - - private static void checkType(ub2 a, ub2 b) { - if (a != b) throw new DatabaseException("type mismatch"); - } - -} - -} - - diff --git a/src/std/database/poly/database.d b/src/std/database/poly/database.d index 6b8b1ac..5f5d8ec 100644 --- a/src/std/database/poly/database.d +++ b/src/std/database/poly/database.d @@ -21,7 +21,7 @@ import std.database.variant; import std.meta; -alias Database(T) = BasicDatabase!(Driver!T,T); +alias Database(T) = BasicDatabase!(Driver!T, T); struct DefaultPolicy { alias Allocator = MyMallocator; @@ -32,13 +32,13 @@ auto registerDatabase(DB)(string name) { PolyDB.register!DB(name); } -auto createDatabase()(string defaultURI="") { - return Database!DefaultPolicy(defaultURI); +auto createDatabase()(string defaultURI = "") { + return Database!DefaultPolicy(defaultURI); } struct Driver(Policy) { alias Allocator = Policy.Allocator; - alias Cell = BasicCell!(Driver,Policy); + alias Cell = BasicCell!(Driver, Policy); alias BindArgs = Array!Variant; // revise using allocator make @@ -52,28 +52,30 @@ struct Driver(Policy) { //return s; // cast apparently necessary or get array cast misallignment } - static auto destruct(T,A)(ref A allocator, void[] data) { + static auto destruct(T, A)(ref A allocator, void[] data) { import core.memory : GC; // needed? .destroy(*(cast(T*) data.ptr)); allocator.deallocate(data); GC.removeRange(data.ptr); } - static auto toTypedPtr(T)(void[] data) {return (cast(T[]) data).ptr;} + static auto toTypedPtr(T)(void[] data) { + return (cast(T[]) data).ptr; + } static struct DBVtable { - void[] function(string defaultURI) create; + void[]function(string defaultURI) create; void function(void[]) destroy; const(Feature[]) function(void[] db) features; // how to make parameter ref const(FeatureArray) } static struct ConVTable { - void[] function(void[], Source source) create; + void[]function(void[], Source source) create; void function(void[]) destroy; } static struct StmtVTable { - void[] function(void[], string sql) create; + void[]function(void[], string sql) create; void function(void[]) destroy; void function(void[] stmt) prepare; void function(void[] stmt) query; @@ -92,19 +94,20 @@ struct Driver(Policy) { alias Database = Driver.Database; alias Connection = Driver.Connection; - static void[] create(string uri) {return construct!Database(alloc, uri);} - static void destroy(void[] db) {destruct!Database(alloc, db);} + static void[] create(string uri) { + return construct!Database(alloc, uri); + } + + static void destroy(void[] db) { + destruct!Database(alloc, db); + } static const(Feature[]) features(void[] db) { //return DB.features; return (cast(Database*) db.ptr).features; } - static DBVtable vtable = { - &create, - &destroy, - &features, - }; + static DBVtable vtable = {&create, &destroy, &features,}; } static struct ConGen(Driver) { @@ -116,15 +119,13 @@ struct Driver(Policy) { return construct!Connection(alloc, toTypedPtr!Database(db), source); } - static void destroy(void[] con) {destruct!Connection(alloc, con);} + static void destroy(void[] con) { + destruct!Connection(alloc, con); + } - static ConVTable vtable = { - &create, - &destroy, - }; + static ConVTable vtable = {&create, &destroy,}; } - static struct StmtGen(Driver) { private static Allocator alloc = MyMallocator(); alias Connection = Driver.Connection; @@ -132,25 +133,26 @@ struct Driver(Policy) { // doesn't work outside of scope - static void constructQuery(int i=0, A...)(Statement* stmt, A a) { + static void constructQuery(int i = 0, A...)(Statement* stmt, A a) { // list of primitive types somewhere ? // dstring causes problems //alias Types = AliasSeq!(byte, ubyte, string, char, dchar, int, uint, long, ulong); alias Types = AliasSeq!(int, string, Date); static void call(int i, T, A...)(Statement* stmt, T v, A a) { - constructQuery!(i+1)(stmt, a[0..i], v, a[(i+1)..$]); + constructQuery!(i + 1)(stmt, a[0 .. i], v, a[(i + 1) .. $]); } static if (i == a.length) { stmt.query(a); - } else { + } + else { //log("type: ", a[i].type); - foreach(T; Types) { + foreach (T; Types) { //log("--TYPE: ", typeid(T)); //if (a[i].type == typeid(T)) if (a[i].convertsTo!T) { - call!i(stmt,a[i].get!T,a); + call!i(stmt, a[i].get!T, a); return; } } @@ -158,26 +160,44 @@ struct Driver(Policy) { } } - static auto create(void[] con, string sql) { return construct!Statement(alloc, toTypedPtr!Connection(con), sql); } - static void destroy(void[] stmt) {destruct!Statement(alloc, stmt);} - static void prepare(void[] stmt) {toTypedPtr!Statement(stmt).prepare();} - static void query(void[] stmt) {toTypedPtr!Statement(stmt).query();} + static void destroy(void[] stmt) { + destruct!Statement(alloc, stmt); + } + static void prepare(void[] stmt) { + toTypedPtr!Statement(stmt).prepare(); + } - static void callVariadic(alias F,S,A...) (ref S s, A a) { + static void query(void[] stmt) { + toTypedPtr!Statement(stmt).query(); + } + + static void callVariadic(alias F, S, A...)(ref S s, A a) { //initial args come last in this call switch (s.length) { - case 0: break; - case 1: F(a,s[0]); break; - case 2: F(a,s[0],s[1]); break; - case 3: F(a,s[0],s[1],s[2]); break; - case 4: F(a,s[0],s[1],s[2],s[3]); break; - case 5: F(a,s[0],s[1],s[2],s[3],s[4]); break; - default: throw new Exception("too many args"); + case 0: + break; + case 1: + F(a, s[0]); + break; + case 2: + F(a, s[0], s[1]); + break; + case 3: + F(a, s[0], s[1], s[2]); + break; + case 4: + F(a, s[0], s[1], s[2], s[3]); + break; + case 5: + F(a, s[0], s[1], s[2], s[3], s[4]); + break; + default: + throw new Exception("too many args"); } } @@ -188,228 +208,249 @@ struct Driver(Policy) { //void F(A...) (A a) {stmt.query(a);} switch (a.length) { - case 0: break; - case 1: constructQuery(s,a[0]); break; - case 2: constructQuery(s,a[0],a[1]); break; - case 3: constructQuery(s,a[0],a[1],a[2]); break; - default: throw new DatabaseException("too many args"); + case 0: + break; + case 1: + constructQuery(s, a[0]); + break; + case 2: + constructQuery(s, a[0], a[1]); + break; + case 3: + constructQuery(s, a[0], a[1], a[2]); + break; + default: + throw new DatabaseException("too many args"); } } static StmtVTable vtable = { - &create, - &destroy, - &prepare, - &query, - &variadicQuery, - }; - } - - //static struct QueryGen(Driver, T...) // but you don't have driver and args at same time + &create, &destroy, &prepare, &query, &variadicQuery,}; + } + //static struct QueryGen(Driver, T...) // but you don't have driver and args at same time - struct Database { - private static Array!DriverInfo drivers; - private string URI; + struct Database { + private static Array!DriverInfo drivers; + private string URI; - private DriverInfo* driver; - private void[] db; + private DriverInfo* driver; + private void[] db; - alias queryVariableType = QueryVariableType.Dollar; + alias queryVariableType = QueryVariableType.Dollar; - static void register(DB) (string name) { - alias Driver = DB.Driver; - DriverInfo driver; - driver.name = name; - driver.dbVtable = DBGen!Driver.vtable; - driver.conVtable = ConGen!Driver.vtable; - driver.stmtVtable = StmtGen!Driver.vtable; + static void register(DB)(string name) { + alias Driver = DB.Driver; + DriverInfo driver; + driver.name = name; + driver.dbVtable = DBGen!Driver.vtable; + driver.conVtable = ConGen!Driver.vtable; + driver.stmtVtable = StmtGen!Driver.vtable; - drivers ~= driver; + drivers ~= driver; - info( - "poly register: ", - "name: ", name, ", " - "type: ", typeid(DB), - "index: ", drivers.length); - } + info("poly register: ", "name: ", name, ", " "type: ", + typeid(DB), "index: ", drivers.length); + } - void setDatabase(string name) { - driver = findDatabase(name); - db = driver.dbVtable.create(URI); - } + void setDatabase(string name) { + driver = findDatabase(name); + db = driver.dbVtable.create(URI); + } - bool isDatabaseSet() {return driver && db;} + bool isDatabaseSet() { + return driver && db; + } - //Allocator allocator; + //Allocator allocator; - const(Feature[]) features() { - // strange feature behavior to get it working - if (driver) return driver.dbVtable.features(db); - static const FeatureArray f = [ - //Feature.InputBinding, + const(Feature[]) features() { + // strange feature behavior to get it working + if (driver) + return driver.dbVtable.features(db); + static const FeatureArray f = [//Feature.InputBinding, //Feature.DateBinding, //Feature.ConnectionPool, //Feature.OutputArrayBinding, ]; - return f; - } + return f; + } - this(string URI_) { - //allocator = Allocator(); - URI = URI_; - } + this(string URI_) { + //allocator = Allocator(); + URI = URI_; + } - ~this() { - if (isDatabaseSet) { - if (!db.ptr) throw new DatabaseException("db not set"); - driver.dbVtable.destroy(db); + ~this() { + if (isDatabaseSet) { + if (!db.ptr) + throw new DatabaseException("db not set"); + driver.dbVtable.destroy(db); + } } - } - private static DriverInfo* findDatabase(string name) { - //import std.algorithm; - //import std.range.primitives: empty; - foreach(ref d; drivers) { - if (d.name == name) return &d; + private static DriverInfo* findDatabase(string name) { + //import std.algorithm; + //import std.range.primitives: empty; + foreach (ref d; drivers) { + if (d.name == name) + return &d; + } + throw new DatabaseException("can't find database with name: " ~ name); } - throw new DatabaseException("can't find database with name: " ~ name); } - } - struct Connection { - Database* database; - Source source; - void[] con; + struct Connection { + Database* database; + Source source; + void[] con; - auto driver() {return database.driver;} - auto db() {return database.db;} + auto driver() { + return database.driver; + } - this(Database* database_, Source source_) { - database = database_; - source = source_; - if (!database.isDatabaseSet) throw new DatabaseException("no database set"); - con = driver.conVtable.create(db, source); - } + auto db() { + return database.db; + } - ~this() { - driver.conVtable.destroy(con); + this(Database* database_, Source source_) { + database = database_; + source = source_; + if (!database.isDatabaseSet) + throw new DatabaseException("no database set"); + con = driver.conVtable.create(db, source); + } + + ~this() { + driver.conVtable.destroy(con); + } } - } - struct Describe { - } + struct Describe { + } - struct Bind { - ValueType type; - } + struct Bind { + ValueType type; + } - struct Statement { - Connection *connection; - string sql; - Allocator *allocator; - void[] stmt; + struct Statement { + Connection* connection; + string sql; + Allocator* allocator; + void[] stmt; - auto driver() {return connection.driver;} - auto con() {return connection.con;} + auto driver() { + return connection.driver; + } - this(Connection* connection_, string sql_) { - connection = connection_; - sql = sql_; - //allocator = &con.db.allocator; - stmt = driver.stmtVtable.create(con, sql); - } + auto con() { + return connection.con; + } - ~this() { - driver.stmtVtable.destroy(stmt); - } + this(Connection* connection_, string sql_) { + connection = connection_; + sql = sql_; + //allocator = &con.db.allocator; + stmt = driver.stmtVtable.create(con, sql); + } - void exec() { - //check("SQLExecDirect", SQLExecDirect(data_.stmt,cast(SQLCHAR*) toStringz(data_.sql), SQL_NTS)); - } + ~this() { + driver.stmtVtable.destroy(stmt); + } - void prepare() { - driver.stmtVtable.prepare(stmt); - } + void exec() { + //check("SQLExecDirect", SQLExecDirect(data_.stmt,cast(SQLCHAR*) toStringz(data_.sql), SQL_NTS)); + } - void query() { - driver.stmtVtable.query(stmt); - } + void prepare() { + driver.stmtVtable.prepare(stmt); + } - void query(A...) (ref A args) { - import std.range; - //auto a = BindArgs(only(args)); - auto a = BindArgs(); - a.reserve(args.length); - foreach(arg; args) a ~= Variant(arg); - driver.stmtVtable.variadicQuery(stmt, a); - } + void query() { + driver.stmtVtable.query(stmt); + } - bool hasRows() {return true;} // fix + void query(A...)(ref A args) { + import std.range; + //auto a = BindArgs(only(args)); + auto a = BindArgs(); + a.reserve(args.length); + foreach (arg; args) + a ~= Variant(arg); + driver.stmtVtable.variadicQuery(stmt, a); + } - void bind(int n, int value) { - } + bool hasRows() { + return true; + } // fix - void bind(int n, const char[] value){ - } + void bind(int n, int value) { + } - void bind(int n, Date d) { - } + void bind(int n, const char[] value) { + } - void reset() {} - } + void bind(int n, Date d) { + } - struct Result { - Statement *stmt; - Allocator *allocator; - Array!Bind bind; + void reset() { + } + } - this(Statement* stmt_, int rowArraySize_) { - stmt = stmt_; - //allocator = &stmt.con.db.allocator; + struct Result { + Statement* stmt; + Allocator* allocator; + Array!Bind bind; - //build_describe(); - //build_bind(); - } + this(Statement* stmt_, int rowArraySize_) { + stmt = stmt_; + //allocator = &stmt.con.db.allocator; - ~this() { - //foreach(ref b; bind) { - //} - } + //build_describe(); + //build_bind(); + } - void build_describe() { - } + ~this() { + //foreach(ref b; bind) { + //} + } - void build_bind() { - } + void build_describe() { + } - int columns() {return 0;} + void build_bind() { + } - //bool hasResult() {return columns != 0;} - bool hasResult() {return 0;} + int columns() { + return 0; + } - int fetch() { - return 0; - } + //bool hasResult() {return columns != 0;} + bool hasResult() { + return 0; + } - ubyte[] rawData(Cell* cell) { - return null; - } + int fetch() { + return 0; + } - Variant getValue(Cell* cell) - { - return Variant; //TODO: - } + ubyte[] rawData(Cell* cell) { + return null; + } + Variant getValue(Cell* cell) { + return Variant; //TODO: + } - bool isNull(Cell* cell){return false;} + bool isNull(Cell* cell) { + return false; + } - auto name(size_t idx) { - return "-name-"; + auto name(size_t idx) { + return "-name-"; + } } - } - -} + } diff --git a/src/std/database/postgres/database.d b/src/std/database/postgres/database.d index 4a4aa38..d288528 100644 --- a/src/std/database/postgres/database.d +++ b/src/std/database/postgres/database.d @@ -23,14 +23,14 @@ struct DefaultPolicy { static const bool nonblocking = false; } -alias Database(T) = BasicDatabase!(Driver!T,T); +alias Database(T) = BasicDatabase!(Driver!T, T); -auto createDatabase()(string defaultURI="") { - return Database!DefaultPolicy(defaultURI); +auto createDatabase()(string defaultURI = "") { + return Database!DefaultPolicy(defaultURI); } -auto createDatabase(T)(string defaultURI="") { - return Database!T(defaultURI); +auto createDatabase(T)(string defaultURI = "") { + return Database!T(defaultURI); } /* @@ -39,41 +39,42 @@ auto createDatabase(T)(string defaultURI="") { } */ - -void error()(PGconn *con, string msg) { +void error()(PGconn* con, string msg) { import std.conv; + auto s = msg ~ to!string(PQerrorMessage(con)); throw new DatabaseException(msg); } -void error()(PGconn *con, string msg, int result) { +void error()(PGconn* con, string msg, int result) { import std.conv; + auto s = "error:" ~ msg ~ ": " ~ to!string(result) ~ ": " ~ to!string(PQerrorMessage(con)); throw new DatabaseException(msg); } -int check()(PGconn *con, string msg, int result) { +int check()(PGconn* con, string msg, int result) { info(msg, ": ", result); - if (result != 1) error(con, msg, result); + if (result != 1) + error(con, msg, result); return result; } -int checkForZero()(PGconn *con, string msg, int result) { +int checkForZero()(PGconn* con, string msg, int result) { info(msg, ": ", result); - if (result != 0) error(con, msg, result); + if (result != 0) + error(con, msg, result); return result; } struct Driver(Policy) { alias Allocator = Policy.Allocator; - alias Cell = BasicCell!(Driver,Policy); + alias Cell = BasicCell!(Driver, Policy); struct Database { static const auto queryVariableType = QueryVariableType.Dollar; - static const FeatureArray features = [ - Feature.InputBinding, - Feature.DateBinding, + static const FeatureArray features = [Feature.InputBinding, Feature.DateBinding, //Feature.ConnectionPool, ]; @@ -90,7 +91,7 @@ struct Driver(Policy) { struct Connection { Database* db; Source source; - PGconn *con; + PGconn* con; this(Database* db_, Source source_) { db = db_; @@ -99,10 +100,10 @@ struct Driver(Policy) { string conninfo; conninfo ~= "dbname=" ~ source.database; con = PQconnectdb(toStringz(conninfo)); - if (PQstatus(con) != CONNECTION_OK) error(con, "login error"); + if (PQstatus(con) != CONNECTION_OK) + error(con, "login error"); } - ~this() { PQfinish(con); } @@ -111,18 +112,19 @@ struct Driver(Policy) { return PQsocket(con); } - void* handle() {return con;} + void* handle() { + return con; + } } - struct Statement { Connection* connection; string sql; - Allocator *allocator; - PGconn *con; + Allocator* allocator; + PGconn* con; string name; - PGresult *prepareRes; - PGresult *res; + PGresult* prepareRes; + PGresult* res; Array!(char*) bindValue; Array!(Oid) bindType; @@ -138,10 +140,10 @@ struct Driver(Policy) { } ~this() { - for(int i = 0; i != bindValue.length; ++i) { + for (int i = 0; i != bindValue.length; ++i) { auto ptr = bindValue[i]; auto length = bindLength[i]; - allocator.deallocate(ptr[0..length]); + allocator.deallocate(ptr[0 .. length]); } } @@ -151,32 +153,29 @@ struct Driver(Policy) { void bind(int n, const char[] value) { } - void query() { import std.conv; info("query sql: ", sql); - if (!prepareRes) prepare(); + if (!prepareRes) + prepare(); auto n = bindValue.length; int resultFormat = 1; static if (Policy.nonblocking) { - checkForZero(con,"PQsetnonblocking", PQsetnonblocking(con, 1)); + checkForZero(con, "PQsetnonblocking", PQsetnonblocking(con, 1)); - check(con, "PQsendQueryPrepared", PQsendQueryPrepared( - con, - toStringz(name), - cast(int) n, - n ? cast(const char **) &bindValue[0] : null, - n ? cast(int*) &bindLength[0] : null, - n ? cast(int*) &bindFormat[0] : null, - resultFormat)); + check(con, "PQsendQueryPrepared", PQsendQueryPrepared(con, + toStringz(name), cast(int) n, + n ? cast(const char**)&bindValue[0] : null, + n ? cast(int*)&bindLength[0] : null, + n ? cast(int*)&bindFormat[0] : null, resultFormat)); do { Policy.Handler handler; - handler.addSocket(posixSocket()); + handler.addSocket(posixSocket()); /* auto s = PQconnectPoll(con); if (s == PGRES_POLLING_OK) { @@ -189,25 +188,23 @@ struct Driver(Policy) { handler.wait(); check(con, "PQconsumeInput", PQconsumeInput(con)); - PGnotify* notify; + PGnotify* notify; while ((notify = PQnotifies(con)) != null) { info("notify: ", to!string(notify.relname)); PQfreemem(notify); } - } while (PQisBusy(con) == 1); + } + while (PQisBusy(con) == 1); res = PQgetResult(con); - } else { - res = PQexecPrepared( - con, - toStringz(name), - cast(int) n, - n ? cast(const char **) &bindValue[0] : null, - n ? cast(int*) &bindLength[0] : null, - n ? cast(int*) &bindFormat[0] : null, - resultFormat); + } + else { + res = PQexecPrepared(con, toStringz(name), cast(int) n, + n ? cast(const char**)&bindValue[0] : null, + n ? cast(int*)&bindLength[0] : null, + n ? cast(int*)&bindFormat[0] : null, resultFormat); } /* @@ -220,7 +217,7 @@ struct Driver(Policy) { // if (!PQsetSingleRowMode(con)) throw error("PQsetSingleRowMode"); } - void query(X...) (X args) { + void query(X...)(X args) { info("query sql: ", sql); // todo: stack allocation @@ -230,7 +227,8 @@ struct Driver(Policy) { bindLength.clear(); bindFormat.clear(); - foreach (ref arg; args) bind(arg); + foreach (ref arg; args) + bind(arg); auto n = bindValue.length; @@ -248,25 +246,26 @@ struct Driver(Policy) { int resultForamt = 0; - res = PQexecParams( - con, - toStringz(sql), - cast(int) n, - n ? cast(Oid*) &bindType[0] : null, - n ? cast(const char **) &bindValue[0] : null, - n ? cast(int*) &bindLength[0] : null, - n ? cast(int*) &bindFormat[0] : null, - resultForamt); + res = PQexecParams(con, toStringz(sql), cast(int) n, + n ? cast(Oid*)&bindType[0] : null, + n ? cast(const char**)&bindValue[0] : null, + n ? cast(int*)&bindLength[0] : null, + n ? cast(int*)&bindFormat[0] : null, resultForamt); } - bool hasRows() {return true;} + bool hasRows() { + return true; + } - int binds() {return cast(int) bindValue.length;} // fix + int binds() { + return cast(int) bindValue.length; + } // fix void bind(string v) { - import core.stdc.string: strncpy; - void[] s = allocator.allocate(v.length+1); - char *p = cast(char*) s.ptr; + import core.stdc.string : strncpy; + + void[] s = allocator.allocate(v.length + 1); + char* p = cast(char*) s.ptr; strncpy(p, v.ptr, v.length); p[v.length] = 0; bindValue ~= p; @@ -277,8 +276,9 @@ struct Driver(Policy) { void bind(int v) { import std.bitmanip; + void[] s = allocator.allocate(int.sizeof); - *cast(int*) s.ptr = peek!(int, Endian.bigEndian)(cast(ubyte[]) (&v)[0..int.sizeof]); + *cast(int*) s.ptr = peek!(int, Endian.bigEndian)(cast(ubyte[])(&v)[0 .. int.sizeof]); bindValue ~= cast(char*) s.ptr; bindType ~= INT4OID; bindLength ~= cast(int) s.length; @@ -288,6 +288,7 @@ struct Driver(Policy) { void bind(Date v) { /* utility functions take 8 byte values but DATEOID is a 4 byte value */ import std.bitmanip; + int[3] mdy; mdy[0] = v.month; mdy[1] = v.day; @@ -295,25 +296,21 @@ struct Driver(Policy) { long d; PGTYPESdate_mdyjul(&mdy[0], &d); void[] s = allocator.allocate(4); - *cast(int*) s.ptr = peek!(int, Endian.bigEndian)(cast(ubyte[]) (&d)[0..4]); + *cast(int*) s.ptr = peek!(int, Endian.bigEndian)(cast(ubyte[])(&d)[0 .. 4]); bindValue ~= cast(char*) s.ptr; bindType ~= DATEOID; bindLength ~= cast(int) s.length; bindFormat ~= 1; } - void prepare() { + void prepare() { const Oid* paramTypes; - prepareRes = PQprepare( - con, - toStringz(name), - toStringz(sql), - 0, - paramTypes); + prepareRes = PQprepare(con, toStringz(name), toStringz(sql), 0, paramTypes); } auto error(string msg) { import std.conv; + string s; s ~= msg ~ ", " ~ to!string(PQerrorMessage(con)); return new DatabaseException(s); @@ -324,7 +321,8 @@ struct Driver(Policy) { private auto posixSocket() { int s = PQsocket(con); - if (s == -1) throw new DatabaseException("can't get socket"); + if (s == -1) + throw new DatabaseException("can't get socket"); return s; } @@ -336,7 +334,6 @@ struct Driver(Policy) { string name; } - struct Bind { ValueType type; int idx; @@ -347,8 +344,8 @@ struct Driver(Policy) { struct Result { Statement* stmt; - PGconn *con; - PGresult *res; + PGconn* con; + PGresult* res; int columns; Array!Describe describe; ExecStatusType status; @@ -371,7 +368,8 @@ struct Driver(Policy) { } ~this() { - if (res) close(); + if (res) + close(); } bool setup() { @@ -386,17 +384,21 @@ struct Driver(Policy) { if (status == PGRES_COMMAND_OK) { close(); return false; - } else if (status == PGRES_EMPTY_QUERY) { + } + else if (status == PGRES_EMPTY_QUERY) { close(); return false; - } else if (status == PGRES_TUPLES_OK) { + } + else if (status == PGRES_TUPLES_OK) { return true; - } else throw error(res,status); + } + else + throw error(res, status); } - void build_describe() { import std.conv; + // called after next() columns = PQnfields(res); for (int col = 0; col != columns; col++) { @@ -411,45 +413,60 @@ struct Driver(Policy) { void build_bind() { // artificial bind setup bind.reserve(columns); - for(int i = 0; i < columns; ++i) { + for (int i = 0; i < columns; ++i) { auto d = &describe[i]; bind ~= Bind(); auto b = &bind.back(); b.type = ValueType.String; b.idx = i; - switch(d.dbType) { - case VARCHAROID: b.type = ValueType.String; break; - case INT4OID: b.type = ValueType.Int; break; - case DATEOID: b.type = ValueType.Date; break; - default: throw new DatabaseException("unsupported type"); + switch (d.dbType) { + case VARCHAROID: + b.type = ValueType.String; + break; + case INT4OID: + b.type = ValueType.Int; + break; + case DATEOID: + b.type = ValueType.Date; + break; + default: + throw new DatabaseException("unsupported type"); } } } int fetch() { - return ++row != rows ? 1 :0; + return ++row != rows ? 1 : 0; } bool singleRownext() { - if (res) PQclear(res); + if (res) + PQclear(res); res = PQgetResult(con); - if (!res) return false; + if (!res) + return false; status = PQresultStatus(res); if (status == PGRES_COMMAND_OK) { close(); return false; - } else if (status == PGRES_SINGLE_TUPLE) return true; + } + else if (status == PGRES_SINGLE_TUPLE) + return true; else if (status == PGRES_TUPLES_OK) { close(); return false; - } else throw error(status); + } + else + throw error(status); } void close() { - if (!res) throw error("couldn't close result: result was not open"); + if (!res) + throw error("couldn't close result: result was not open"); res = PQgetResult(con); - if (res) throw error("couldn't close result: was not finished"); + if (res) + throw error("couldn't close result: was not finished"); res = null; } @@ -459,16 +476,16 @@ struct Driver(Policy) { auto error(ExecStatusType status) { import std.conv; + string s = "result error: " ~ to!string(PQresStatus(status)); return new DatabaseException(s); } - auto error(PGresult *res, ExecStatusType status) { + auto error(PGresult* res, ExecStatusType status) { import std.conv; + const char* msg = PQresultErrorMessage(res); - string s = - "error: " ~ to!string(PQresStatus(status)) ~ - ", message:" ~ to!string(msg); + string s = "error: " ~ to!string(PQresStatus(status)) ~ ", message:" ~ to!string(msg); return new DatabaseException(s); } @@ -483,56 +500,69 @@ struct Driver(Policy) { return describe[idx].name; } - ubyte[] rawData(Cell* cell){ - auto ptr = cast(ubyte*) data(cell.bind.idx); - return ptr[0..len(cell.bind.idx)]; - } - - Variant getValue(Cell* cell) - { - Variant value; - switch(type(cell.bind.idx)) - { - case VARCHAROID: - { - immutable char *ptr = cast(immutable char*) data(cell.bind.idx); - value = cast(string) ptr[0..len(cell.bind.idx)]; - } - break; - case INT4OID: - { - import std.bitmanip; - auto p = cast(ubyte*) data(cell.bind.idx); - value = bigEndianToNative!int(p[0..int.sizeof]); - } - break; - case DATEOID: - { - import std.bitmanip; - auto ptr = cast(ubyte*) data(cell.bind.idx); - int sz = len(cell.bind.idx); - date d = bigEndianToNative!uint(ptr[0..4]); // why not sz? - int[3] mdy; - PGTYPESdate_julmdy(d, &mdy[0]); - value = Date(mdy[2],mdy[0],mdy[1]); - } - break; - } - return value; //TODO: - } - - - bool isNull(Cell* cell){return false;} + ubyte[] rawData(Cell* cell) { + auto ptr = cast(ubyte*) data(cell.bind.idx); + return ptr[0 .. len(cell.bind.idx)]; + } + + Variant getValue(Cell* cell) { + Variant value; + switch (type(cell.bind.idx)) { + case VARCHAROID: { + immutable char* ptr = cast(immutable char*) data(cell.bind.idx); + value = cast(string) ptr[0 .. len(cell.bind.idx)]; + } + break; + case INT4OID: { + import std.bitmanip; + + auto p = cast(ubyte*) data(cell.bind.idx); + value = bigEndianToNative!int(p[0 .. int.sizeof]); + } + break; + case DATEOID: { + import std.bitmanip; + + auto ptr = cast(ubyte*) data(cell.bind.idx); + int sz = len(cell.bind.idx); + date d = bigEndianToNative!uint(ptr[0 .. 4]); // why not sz? + int[3] mdy; + PGTYPESdate_julmdy(d, &mdy[0]); + value = Date(mdy[2], mdy[0], mdy[1]); + } + break; + } + return value; //TODO: + } + + bool isNull(Cell* cell) { + return false; + } void checkType(int a, int b) { - if (a != b) throw new DatabaseException("type mismatch"); + if (a != b) + throw new DatabaseException("type mismatch"); + } + + void* data(int col) { + return PQgetvalue(res, row, col); + } + + bool isNull(int col) { + return PQgetisnull(res, row, col) != 0; } - void* data(int col) {return PQgetvalue(res, row, col);} - bool isNull(int col) {return PQgetisnull(res, row, col) != 0;} - int type(int col) {return describe[col].dbType;} - int fmt(int col) {return describe[col].fmt;} - int len(int col) {return PQgetlength(res, row, col);} + int type(int col) { + return describe[col].dbType; + } + + int fmt(int col) { + return describe[col].fmt; + } + + int len(int col) { + return PQgetlength(res, row, col); + } } } diff --git a/src/std/database/reference/database.d b/src/std/database/reference/database.d index 751b54c..47afa55 100644 --- a/src/std/database/reference/database.d +++ b/src/std/database/reference/database.d @@ -18,31 +18,31 @@ struct DefaultPolicy { // this function is module specific because it allows // a default template to be specified -auto createDatabase()(string defaultUrl=null) { - return Database!DefaultPolicy(defaultUrl); +auto createDatabase()(string defaultUrl = null) { + return Database!DefaultPolicy(defaultUrl); } //one for specfic type -auto createDatabase(T)(string defaultUrl=null) { - return Database!T(defaultUrl); +auto createDatabase(T)(string defaultUrl = null) { + return Database!T(defaultUrl); } // these functions can be moved into util once a solution to forward bug is found auto connection(T)(Database!T db, string source) { - return Connection!T(db,source); + return Connection!T(db, source); } -auto statement(T) (Connection!T con, string sql) { +auto statement(T)(Connection!T con, string sql) { return Statement!T(con, sql); } -auto statement(T, X...) (Connection!T con, string sql, X args) { +auto statement(T, X...)(Connection!T con, string sql, X args) { return Statement!T(con, sql, args); } auto result(T)(Statement!T stmt) { - return Result!T(stmt); + return Result!T(stmt); } struct Database(T) { @@ -50,7 +50,6 @@ struct Database(T) { static const auto queryVariableType = QueryVariableType.QuestionMark; - private struct Payload { string defaultURI; Allocator allocator; @@ -65,8 +64,13 @@ struct Database(T) { info("closing database resource"); } - this(this) { assert(false); } - void opAssign(Database.Payload rhs) { assert(false); } + this(this) { + assert(false); + } + + void opAssign(Database.Payload rhs) { + assert(false); + } } private alias RefCounted!(Payload, RefCountedAutoInitialize.no) Data; @@ -77,21 +81,31 @@ struct Database(T) { } } - struct Connection(T) { alias Database = .Database!T; //alias Statement = .Statement!T; - auto statement(string sql) {return Statement!T(this,sql);} - auto statement(X...) (string sql, X args) {return Statement!T(this,sql,args);} - auto query(string sql) {return statement(sql).query();} - auto query(T...) (string sql, T args) {return statement(sql).query(args);} + auto statement(string sql) { + return Statement!T(this, sql); + } + + auto statement(X...)(string sql, X args) { + return Statement!T(this, sql, args); + } + + auto query(string sql) { + return statement(sql).query(); + } + + auto query(T...)(string sql, T args) { + return statement(sql).query(args); + } package this(Database db, string source) { - data_ = Data(db,source); + data_ = Data(db, source); } - private: +private: struct Payload { Database db; @@ -107,8 +121,13 @@ struct Connection(T) { ~this() { } - this(this) { assert(false); } - void opAssign(Connection.Payload rhs) { assert(false); } + this(this) { + assert(false); + } + + void opAssign(Connection.Payload rhs) { + assert(false); + } } private alias RefCounted!(Payload, RefCountedAutoInitialize.no) Data; @@ -122,18 +141,23 @@ struct Statement(T) { //alias Range = Result.Range; // error Result.Payload no size yet for forward reference // temporary - auto result() {return Result!T(this);} - auto opSlice() {return result();} + auto result() { + return Result!T(this); + } + + auto opSlice() { + return result(); + } this(Connection con, string sql) { - data_ = Data(con,sql); + data_ = Data(con, sql); //prepare(); // must be able to detect binds in all DBs //if (!data_.binds) query(); } - this(T...) (Connection con, string sql, T args) { - data_ = Data(con,sql); + this(T...)(Connection con, string sql, T args) { + data_ = Data(con, sql); //prepare(); //bindAll(args); //query(); @@ -145,10 +169,10 @@ struct Statement(T) { void bind(int n, int value) { } - void bind(int n, const char[] value){ + void bind(int n, const char[] value) { } - private: +private: struct Payload { Connection con; @@ -161,29 +185,37 @@ struct Statement(T) { sql = sql_; } - this(this) { assert(false); } - void opAssign(Statement.Payload rhs) { assert(false); } + this(this) { + assert(false); + } + + void opAssign(Statement.Payload rhs) { + assert(false); + } } alias RefCounted!(Payload, RefCountedAutoInitialize.no) Data; Data data_; - public: +public: + + void exec() { + } - void exec() {} - void prepare() {} + void prepare() { + } auto query() { return result(); } - auto query(X...) (X args) { + auto query(X...)(X args) { return query(); } - private: +private: - void bindAll(T...) (T args) { + void bindAll(T...)(T args) { int col; foreach (arg; args) { bind(++col, arg); @@ -204,12 +236,19 @@ struct Result(T) { data_ = Data(stmt); } - auto opSlice() {return ResultRange(this);} + auto opSlice() { + return ResultRange(this); + } + + bool start() { + return true; + } - bool start() {return true;} - bool next() {return data_.next();} + bool next() { + return data_.next(); + } - private: +private: struct Payload { Statement stmt; @@ -223,8 +262,13 @@ struct Result(T) { return false; } - this(this) { assert(false); } - void opAssign(Statement.Payload rhs) { assert(false); } + this(this) { + assert(false); + } + + void opAssign(Statement.Payload rhs) { + assert(false); + } } alias RefCounted!(Payload, RefCountedAutoInitialize.no) Data; @@ -233,15 +277,17 @@ struct Result(T) { } struct Value { - auto as(T:int)() { + auto as(T : int)() { return 0; } - auto as(T:string)() { + auto as(T : string)() { return "value"; } - auto chars() {return "value";} + auto chars() { + return "value"; + } } struct ResultRange(T) { @@ -255,19 +301,33 @@ struct ResultRange(T) { ok_ = result_.start(); } - bool empty() {return !ok_;} - Row front() {return Row(&result_);} - void popFront() {ok_ = result_.next();} -} + bool empty() { + return !ok_; + } + Row front() { + return Row(&result_); + } + + void popFront() { + ok_ = result_.next(); + } +} struct Row(T) { alias Result = .Result!T; alias Value = .Value; - this(Result* result) { result_ = result;} - Value opIndex(size_t idx) {return Value();} + this(Result* result) { + result_ = result; + } + + Value opIndex(size_t idx) { + return Value(); + } + private Result* result_; - int columns() {return 0;} + int columns() { + return 0; + } } - diff --git a/src/std/database/sqlite/database.d b/src/std/database/sqlite/database.d index 5c4baf7..616ad71 100644 --- a/src/std/database/sqlite/database.d +++ b/src/std/database/sqlite/database.d @@ -24,28 +24,26 @@ struct DefaultPolicy { alias Allocator = MyMallocator; } -alias Database(T) = BasicDatabase!(Driver!T,T); +alias Database(T) = BasicDatabase!(Driver!T, T); -auto createDatabase()(string defaultURI="") { - return Database!DefaultPolicy(defaultURI); +auto createDatabase()(string defaultURI = "") { + return Database!DefaultPolicy(defaultURI); } -auto createDatabase(T)(string defaultURI="") { - return Database!T(defaultURI); +auto createDatabase(T)(string defaultURI = "") { + return Database!T(defaultURI); } struct Driver(Policy) { alias Allocator = Policy.Allocator; - alias Cell = BasicCell!(Driver,Policy); + alias Cell = BasicCell!(Driver, Policy); struct Database { alias queryVariableType = QueryVariableType.QuestionMark; - static const FeatureArray features = [ - Feature.InputBinding, - //Feature.DateBinding, - //Feature.ConnectionPool, - ]; + static const FeatureArray features = [Feature.InputBinding,//Feature.DateBinding, + //Feature.ConnectionPool, + ]; this(string defaultSource_) { } @@ -93,7 +91,7 @@ struct Driver(Policy) { string sql; State state; sqlite3* sq; - sqlite3_stmt *st; + sqlite3_stmt* st; bool hasRows; int binds_; @@ -112,28 +110,23 @@ struct Driver(Policy) { } } - void bind(int col, int value){ - int rc = sqlite3_bind_int( - st, - col, - value); + void bind(int col, int value) { + int rc = sqlite3_bind_int(st, col, value); if (rc != SQLITE_OK) { throw_error("sqlite3_bind_int"); } } - void bind(int col, const char[] value){ - if(value is null) { + void bind(int col, const char[] value) { + if (value is null) { int rc = sqlite3_bind_null(st, col); - if (rc != SQLITE_OK) throw_error("bind1"); - } else { + if (rc != SQLITE_OK) + throw_error("bind1"); + } + else { //cast(void*)-1); - int rc = sqlite3_bind_text( - st, - col, - value.ptr, - cast(int) value.length, - null); + int rc = sqlite3_bind_text(st, col, value.ptr, cast(int) value.length, + null); if (rc != SQLITE_OK) { writeln(rc); throw_error("bind2"); @@ -145,49 +138,53 @@ struct Driver(Policy) { throw new DatabaseException("Date input binding not yet implemented"); } - int binds() {return binds_;} + int binds() { + return binds_; + } void prepare() { - if (!st) { - int res = sqlite3_prepare_v2( - sq, - toStringz(sql), - cast(int) sql.length + 1, - &st, - null); - if (res != SQLITE_OK) throw_error(sq, "prepare", res); + if (!st) { + int res = sqlite3_prepare_v2(sq, toStringz(sql), + cast(int) sql.length + 1, &st, null); + if (res != SQLITE_OK) + throw_error(sq, "prepare", res); binds_ = sqlite3_bind_parameter_count(st); } } void query() { //if (state == State.Execute) throw new DatabaseException("already executed"); // restore - if (state == State.Execute) return; + if (state == State.Execute) + return; state = State.Execute; int status = sqlite3_step(st); info("sqlite3_step: status: ", status); if (status == SQLITE_ROW) { hasRows = true; - } else if (status == SQLITE_DONE) { + } + else if (status == SQLITE_DONE) { reset(); - } else { + } + else { throw_error(sq, "step error", status); } } - void query(X...) (X args) { + void query(X...)(X args) { bindAll(args); query(); } - private void bindAll(T...) (T args) { + private void bindAll(T...)(T args) { int col; - foreach (arg; args) bind(++col, arg); + foreach (arg; args) + bind(++col, arg); } void reset() { int status = sqlite3_reset(st); - if (status != SQLITE_OK) throw new DatabaseException("sqlite3_reset error"); + if (status != SQLITE_OK) + throw new DatabaseException("sqlite3_reset error"); } } @@ -199,7 +196,7 @@ struct Driver(Policy) { struct Result { private Statement* stmt_; - private sqlite3_stmt *st_; + private sqlite3_stmt* st_; int columns; int status_; @@ -213,7 +210,7 @@ struct Driver(Policy) { // artificial bind setup bind.reserve(columns); - for(int i = 0; i < columns; ++i) { + for (int i = 0; i < columns; ++i) { bind ~= Bind(); auto b = &bind.back(); b.type = ValueType.String; @@ -223,11 +220,14 @@ struct Driver(Policy) { //~this() {} - bool hasRows() {return stmt_.hasRows;} + bool hasRows() { + return stmt_.hasRows; + } int fetch() { status_ = sqlite3_step(st_); - if (status_ == SQLITE_ROW) return 1; + if (status_ == SQLITE_ROW) + return 1; if (status_ == SQLITE_DONE) { stmt_.reset(); return 0; @@ -237,48 +237,51 @@ struct Driver(Policy) { } auto name(size_t idx) { - import core.stdc.string: strlen; + import core.stdc.string : strlen; + auto ptr = sqlite3_column_name(st_, cast(int) idx); - return cast(string) ptr[0..strlen(ptr)]; + return cast(string) ptr[0 .. strlen(ptr)]; + } + + ubyte[] rawData(Cell* cell) { + return null; //TODO: fix + } + + Variant getValue(Cell* cell) { + Variant value; + int idx = cast(int) cell.bind.idx; + switch (sqlite3_column_type(st_, idx)) { + case SQLITE_INTEGER: + value = sqlite3_column_int(st_, idx); + break; + case SQLITE_FLOAT: + value = sqlite3_column_double(st_, idx); + break; + case SQLITE3_TEXT: { + import core.stdc.string : strlen; + + auto ptr = cast(immutable char*) sqlite3_column_text(st_, + cast(int) cell.bind.idx); + value = cast(string) ptr[0 .. strlen(ptr)]; // fix with length + } + break; + case SQLITE_BLOB: + break; + case SQLITE_NULL: + break; + } + return value; //TODO: } - ubyte[] rawData(Cell* cell) { - return null; //TODO: fix - } - - Variant getValue(Cell* cell){ - Variant value; - int idx = cast(int)cell.bind.idx; - switch(sqlite3_column_type(st_,idx)) - { - case SQLITE_INTEGER: - value = sqlite3_column_int(st_, idx); - break; - case SQLITE_FLOAT: - value = sqlite3_column_double(st_,idx); - break; - case SQLITE3_TEXT: - { - import core.stdc.string: strlen; - auto ptr = cast(immutable char*) sqlite3_column_text(st_, cast(int) cell.bind.idx); - value = cast(string) ptr[0..strlen(ptr)]; // fix with length - } - break; - case SQLITE_BLOB: - break; - case SQLITE_NULL: - break; - } - return value; //TODO: - } - - - bool isNull(Cell* cell){return sqlite3_column_type(st_,cast(int)cell.bind.idx) == SQLITE_NULL;} + bool isNull(Cell* cell) { + return sqlite3_column_type(st_, cast(int) cell.bind.idx) == SQLITE_NULL; + } } - private static void throw_error()(sqlite3 *sq, string msg, int ret) { + private static void throw_error()(sqlite3* sq, string msg, int ret) { import std.conv; - import core.stdc.string: strlen; + import core.stdc.string : strlen; + const(char*) err = sqlite3_errmsg(sq); throw new DatabaseException("sqlite error: " ~ msg ~ ": " ~ to!string(err)); // need to send err } @@ -287,13 +290,15 @@ struct Driver(Policy) { throw new DatabaseException(label); } - private static void throw_error()(string label, char *msg) { + private static void throw_error()(string label, char* msg) { // frees up pass char * as required by sqlite import core.stdc.string : strlen; + char[] m; sizediff_t sz = strlen(msg); m.length = sz; - for(int i = 0; i != sz; i++) m[i] = msg[i]; + for (int i = 0; i != sz; i++) + m[i] = msg[i]; sqlite3_free(msg); throw new DatabaseException(label ~ m.idup); } @@ -321,8 +326,8 @@ return sqlite3_column_text(result_.st_, cast(int) idx_); */ -extern(C) int sqlite_callback(void* cb, int howmany, char** text, char** columns) { - return 0; -} + extern (C) int sqlite_callback(void* cb, int howmany, char** text, char** columns) { + return 0; + } } From ce062bac2a3949308a2b921b738647912aa3d8a4 Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Mon, 4 Jul 2016 16:59:49 +0800 Subject: [PATCH 08/30] add pgsql more type support --- src/std/database/mysql/database.d | 2 + src/std/database/postgres/database.d | 174 +++++++++++++++++++++++++-- src/std/database/uri.d | 3 +- 3 files changed, 170 insertions(+), 9 deletions(-) diff --git a/src/std/database/mysql/database.d b/src/std/database/mysql/database.d index 6bee8e4..f7830c7 100644 --- a/src/std/database/mysql/database.d +++ b/src/std/database/mysql/database.d @@ -448,6 +448,7 @@ private struct Driver(Policy) { Variant getValue(Cell* cell) { Variant value; + if(isNull(cell)) return value; switch (cell.bind.type) { case ValueType.Char: { auto t = (*cast(char*) cell.bind.data.ptr); @@ -906,6 +907,7 @@ private struct Driver(Policy) { Variant getValue(Cell* cell) { Variant value; + if(isNull(cell)) return value; switch (cell.bind.type) { case ValueType.Char: value = (*cast(char*) cell.bind.data.ptr); diff --git a/src/std/database/postgres/database.d b/src/std/database/postgres/database.d index d288528..9729601 100644 --- a/src/std/database/postgres/database.d +++ b/src/std/database/postgres/database.d @@ -420,15 +420,42 @@ struct Driver(Policy) { b.type = ValueType.String; b.idx = i; switch (d.dbType) { + case CHAROID: + b.type = ValueType.Char; + break; + case TEXTOID: + case NAMEOID: case VARCHAROID: b.type = ValueType.String; break; + case BOOLOID: case INT4OID: b.type = ValueType.Int; break; + case INT2OID: + b.type = ValueType.Short; + break; + case INT8OID: + b.type = ValueType.Long; + break; + case FLOAT4OID: + b.type = ValueType.Float; + break; + case FLOAT8OID: + b.type = ValueType.Double; + break; case DATEOID: b.type = ValueType.Date; break; + case TIMEOID: + b.type = ValueType.Time; + break; + case TIMESTAMPOID: + b.type = ValueType.DateTime; + break; + case BYTEAOID: + b.type = ValueType.Raw; + break; default: throw new DatabaseException("unsupported type"); } @@ -506,20 +533,54 @@ struct Driver(Policy) { } Variant getValue(Cell* cell) { + import std.bitmanip; + Variant value; + if(isNull(cell)) return value; + + void * dt = data(cell.bind.idx); + int leng = len(cell.bind.idx); switch (type(cell.bind.idx)) { case VARCHAROID: { - immutable char* ptr = cast(immutable char*) data(cell.bind.idx); - value = cast(string) ptr[0 .. len(cell.bind.idx)]; + immutable char* ptr = cast(immutable char*) dt; + value = cast(string) ptr[0 .. leng]; } break; + case INT2OID:{ + auto t = cast(ubyte *) dt; + ubyte[short.sizeof] data ; + data[] = t[0..short.sizeof]; + value = bigEndianToNative!short(data); + } + break; case INT4OID: { - import std.bitmanip; - - auto p = cast(ubyte*) data(cell.bind.idx); - value = bigEndianToNative!int(p[0 .. int.sizeof]); + auto t = cast(ubyte *) dt; + ubyte[int.sizeof] data ; + data[] = t[0..int.sizeof]; + value = bigEndianToNative!int(data); } break; + case INT8OID: { + auto t = cast(ubyte *) dt; + ubyte[long.sizeof] data ; + data[] = t[0..long.sizeof]; + value = bigEndianToNative!long(data); + } + break; + case FLOAT4OID: { + auto t = cast(ubyte *) dt; + ubyte[float.sizeof] data ; + data[] = t[0..float.sizeof]; + value = bigEndianToNative!float(data); + } + break; + case FLOAT8OID: { + auto t = cast(ubyte *) dt; + ubyte[double.sizeof] data ; + data[] = t[0..double.sizeof]; + value = bigEndianToNative!double(data); + } + break; case DATEOID: { import std.bitmanip; @@ -531,12 +592,58 @@ struct Driver(Policy) { value = Date(mdy[2], mdy[0], mdy[1]); } break; + case TIMESTAMPOID: { + import std.string; + immutable char* ptr = cast(immutable char*) dt; + auto str = cast(string) ptr[0 .. leng]; + value = DateTime.fromISOExtString( str.translate( [ ' ': 'T' ] ).split( '.' ).front() ); + } + break; + case TIMEOID: + { + immutable char* ptr = cast(immutable char*) dt; + auto str = cast(string) ptr[0 .. leng]; + value = parseTimeoid(str); + } + break; + case BYTEAOID: + { + immutable char* ptr = cast(immutable char*) dt; + auto str = cast(string) ptr[0 .. leng]; + value = byteaToUbytes(str); + } + break; + case CHAROID: + { + auto t = cast(char *) dt; + value = cast(char)(leng > 0 ? t[0] : 0x00); + } + break; + case BOOLOID: + { + immutable char* ptr = cast(immutable char*) dt; + auto str = cast(string) ptr[0 .. leng]; + if( s == "true" || s == "t" || s == "1" ) + value = true; + else if( s == "false" || s == "f" || s == "0" ) + value = false; + else + { + auto t = cast(ubyte *) ptr; + ubyte[int.sizeof] data ; + data[] = t[0..int.sizeof]; + value = (bigEndianToNative!int(data) > 0); + } + } + break; + default: + break; } - return value; //TODO: + return value; } bool isNull(Cell* cell) { - return false; + return PQgetisnull(res, row, cell.bind.idx) != 0; } void checkType(int a, int b) { @@ -566,3 +673,54 @@ struct Driver(Policy) { } } + +// the under is from ddbc: +import core.vararg; +import std.exception; +import std.meta; +import std.range.primitives; +import std.traits; +import std.format; + +Time parseTimeoid(const string timeoid) +{ + import std.format; + string input = timeoid.dup; + int hour, min, sec; + formattedRead(input, "%s:%s:%s", &hour, &min, &sec); + return Time(hour, min, sec); +} + +ubyte[] byteaToUbytes(string s) { + if (s is null) + return null; + ubyte[] res; + bool lastBackSlash = 0; + foreach(ch; s) { + if (ch == '\\') { + if (lastBackSlash) { + res ~= '\\'; + lastBackSlash = false; + } else { + lastBackSlash = true; + } + } else { + if (lastBackSlash) { + if (ch == '0') { + res ~= 0; + } else if (ch == 'r') { + res ~= '\r'; + } else if (ch == 'n') { + res ~= '\n'; + } else if (ch == 't') { + res ~= '\t'; + } else { + } + } else { + res ~= cast(byte)ch; + } + lastBackSlash = false; + } + } + return res; +} diff --git a/src/std/database/uri.d b/src/std/database/uri.d index aa72089..aa375f6 100644 --- a/src/std/database/uri.d +++ b/src/std/database/uri.d @@ -61,7 +61,8 @@ URI toURI(string str) { auto n = e[0..j], v = e[j+1..$]; uri.query[n] ~= v; //needs url decoding } - + trace("host is :", uri.host, " port is :", uri.port, " protool is : ", uri.protocol); + trace("path is : ", uri.path); return uri; } From 027b91581142954a625d4a6a2b9e91e3cc725f78 Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Mon, 4 Jul 2016 17:00:54 +0800 Subject: [PATCH 09/30] dfmt style --- src/std/database/mysql/database.d | 6 +- src/std/database/postgres/database.d | 300 ++++++++++++++------------- 2 files changed, 156 insertions(+), 150 deletions(-) diff --git a/src/std/database/mysql/database.d b/src/std/database/mysql/database.d index f7830c7..36990ec 100644 --- a/src/std/database/mysql/database.d +++ b/src/std/database/mysql/database.d @@ -448,7 +448,8 @@ private struct Driver(Policy) { Variant getValue(Cell* cell) { Variant value; - if(isNull(cell)) return value; + if (isNull(cell)) + return value; switch (cell.bind.type) { case ValueType.Char: { auto t = (*cast(char*) cell.bind.data.ptr); @@ -907,7 +908,8 @@ private struct Driver(Policy) { Variant getValue(Cell* cell) { Variant value; - if(isNull(cell)) return value; + if (isNull(cell)) + return value; switch (cell.bind.type) { case ValueType.Char: value = (*cast(char*) cell.bind.data.ptr); diff --git a/src/std/database/postgres/database.d b/src/std/database/postgres/database.d index 9729601..82f27dc 100644 --- a/src/std/database/postgres/database.d +++ b/src/std/database/postgres/database.d @@ -420,42 +420,42 @@ struct Driver(Policy) { b.type = ValueType.String; b.idx = i; switch (d.dbType) { - case CHAROID: - b.type = ValueType.Char; - break; - case TEXTOID: - case NAMEOID: + case CHAROID: + b.type = ValueType.Char; + break; + case TEXTOID: + case NAMEOID: case VARCHAROID: b.type = ValueType.String; break; - case BOOLOID: + case BOOLOID: case INT4OID: b.type = ValueType.Int; break; - case INT2OID: - b.type = ValueType.Short; - break; - case INT8OID: - b.type = ValueType.Long; - break; - case FLOAT4OID: - b.type = ValueType.Float; - break; - case FLOAT8OID: - b.type = ValueType.Double; - break; + case INT2OID: + b.type = ValueType.Short; + break; + case INT8OID: + b.type = ValueType.Long; + break; + case FLOAT4OID: + b.type = ValueType.Float; + break; + case FLOAT8OID: + b.type = ValueType.Double; + break; case DATEOID: b.type = ValueType.Date; break; - case TIMEOID: - b.type = ValueType.Time; - break; - case TIMESTAMPOID: - b.type = ValueType.DateTime; - break; - case BYTEAOID: - b.type = ValueType.Raw; - break; + case TIMEOID: + b.type = ValueType.Time; + break; + case TIMESTAMPOID: + b.type = ValueType.DateTime; + break; + case BYTEAOID: + b.type = ValueType.Raw; + break; default: throw new DatabaseException("unsupported type"); } @@ -533,54 +533,55 @@ struct Driver(Policy) { } Variant getValue(Cell* cell) { - import std.bitmanip; + import std.bitmanip; Variant value; - if(isNull(cell)) return value; + if (isNull(cell)) + return value; - void * dt = data(cell.bind.idx); - int leng = len(cell.bind.idx); + void* dt = data(cell.bind.idx); + int leng = len(cell.bind.idx); switch (type(cell.bind.idx)) { case VARCHAROID: { immutable char* ptr = cast(immutable char*) dt; value = cast(string) ptr[0 .. leng]; } break; - case INT2OID:{ - auto t = cast(ubyte *) dt; - ubyte[short.sizeof] data ; - data[] = t[0..short.sizeof]; - value = bigEndianToNative!short(data); - } - break; + case INT2OID: { + auto t = cast(ubyte*) dt; + ubyte[short.sizeof] data; + data[] = t[0 .. short.sizeof]; + value = bigEndianToNative!short(data); + } + break; case INT4OID: { - auto t = cast(ubyte *) dt; - ubyte[int.sizeof] data ; - data[] = t[0..int.sizeof]; - value = bigEndianToNative!int(data); + auto t = cast(ubyte*) dt; + ubyte[int.sizeof] data; + data[] = t[0 .. int.sizeof]; + value = bigEndianToNative!int(data); + } + break; + case INT8OID: { + auto t = cast(ubyte*) dt; + ubyte[long.sizeof] data; + data[] = t[0 .. long.sizeof]; + value = bigEndianToNative!long(data); + } + break; + case FLOAT4OID: { + auto t = cast(ubyte*) dt; + ubyte[float.sizeof] data; + data[] = t[0 .. float.sizeof]; + value = bigEndianToNative!float(data); + } + break; + case FLOAT8OID: { + auto t = cast(ubyte*) dt; + ubyte[double.sizeof] data; + data[] = t[0 .. double.sizeof]; + value = bigEndianToNative!double(data); } break; - case INT8OID: { - auto t = cast(ubyte *) dt; - ubyte[long.sizeof] data ; - data[] = t[0..long.sizeof]; - value = bigEndianToNative!long(data); - } - break; - case FLOAT4OID: { - auto t = cast(ubyte *) dt; - ubyte[float.sizeof] data ; - data[] = t[0..float.sizeof]; - value = bigEndianToNative!float(data); - } - break; - case FLOAT8OID: { - auto t = cast(ubyte *) dt; - ubyte[double.sizeof] data ; - data[] = t[0..double.sizeof]; - value = bigEndianToNative!double(data); - } - break; case DATEOID: { import std.bitmanip; @@ -592,58 +593,54 @@ struct Driver(Policy) { value = Date(mdy[2], mdy[0], mdy[1]); } break; - case TIMESTAMPOID: { - import std.string; - immutable char* ptr = cast(immutable char*) dt; - auto str = cast(string) ptr[0 .. leng]; - value = DateTime.fromISOExtString( str.translate( [ ' ': 'T' ] ).split( '.' ).front() ); - } - break; - case TIMEOID: - { - immutable char* ptr = cast(immutable char*) dt; - auto str = cast(string) ptr[0 .. leng]; - value = parseTimeoid(str); - } - break; - case BYTEAOID: - { - immutable char* ptr = cast(immutable char*) dt; - auto str = cast(string) ptr[0 .. leng]; - value = byteaToUbytes(str); - } - break; - case CHAROID: - { - auto t = cast(char *) dt; - value = cast(char)(leng > 0 ? t[0] : 0x00); - } - break; - case BOOLOID: - { - immutable char* ptr = cast(immutable char*) dt; - auto str = cast(string) ptr[0 .. leng]; - if( s == "true" || s == "t" || s == "1" ) - value = true; - else if( s == "false" || s == "f" || s == "0" ) - value = false; - else - { - auto t = cast(ubyte *) ptr; - ubyte[int.sizeof] data ; - data[] = t[0..int.sizeof]; - value = (bigEndianToNative!int(data) > 0); - } - } - break; - default: - break; + case TIMESTAMPOID: { + import std.string; + + immutable char* ptr = cast(immutable char*) dt; + auto str = cast(string) ptr[0 .. leng]; + value = DateTime.fromISOExtString(str.translate([' ' : 'T']).split('.').front()); + } + break; + case TIMEOID: { + immutable char* ptr = cast(immutable char*) dt; + auto str = cast(string) ptr[0 .. leng]; + value = parseTimeoid(str); + } + break; + case BYTEAOID: { + immutable char* ptr = cast(immutable char*) dt; + auto str = cast(string) ptr[0 .. leng]; + value = byteaToUbytes(str); + } + break; + case CHAROID: { + auto t = cast(char*) dt; + value = cast(char)(leng > 0 ? t[0] : 0x00); + } + break; + case BOOLOID: { + immutable char* ptr = cast(immutable char*) dt; + auto str = cast(string) ptr[0 .. leng]; + if (s == "true" || s == "t" || s == "1") + value = true; + else if (s == "false" || s == "f" || s == "0") + value = false; + else { + auto t = cast(ubyte*) ptr; + ubyte[int.sizeof] data; + data[] = t[0 .. int.sizeof]; + value = (bigEndianToNative!int(data) > 0); + } + } + break; + default: + break; } return value; } bool isNull(Cell* cell) { - return PQgetisnull(res, row, cell.bind.idx) != 0; + return PQgetisnull(res, row, cell.bind.idx) != 0; } void checkType(int a, int b) { @@ -682,45 +679,52 @@ import std.range.primitives; import std.traits; import std.format; -Time parseTimeoid(const string timeoid) -{ - import std.format; - string input = timeoid.dup; - int hour, min, sec; - formattedRead(input, "%s:%s:%s", &hour, &min, &sec); - return Time(hour, min, sec); +Time parseTimeoid(const string timeoid) { + import std.format; + + string input = timeoid.dup; + int hour, min, sec; + formattedRead(input, "%s:%s:%s", &hour, &min, &sec); + return Time(hour, min, sec); } ubyte[] byteaToUbytes(string s) { - if (s is null) - return null; - ubyte[] res; - bool lastBackSlash = 0; - foreach(ch; s) { - if (ch == '\\') { - if (lastBackSlash) { - res ~= '\\'; - lastBackSlash = false; - } else { - lastBackSlash = true; - } - } else { - if (lastBackSlash) { - if (ch == '0') { - res ~= 0; - } else if (ch == 'r') { - res ~= '\r'; - } else if (ch == 'n') { - res ~= '\n'; - } else if (ch == 't') { - res ~= '\t'; - } else { - } - } else { - res ~= cast(byte)ch; - } - lastBackSlash = false; - } - } - return res; + if (s is null) + return null; + ubyte[] res; + bool lastBackSlash = 0; + foreach (ch; s) { + if (ch == '\\') { + if (lastBackSlash) { + res ~= '\\'; + lastBackSlash = false; + } + else { + lastBackSlash = true; + } + } + else { + if (lastBackSlash) { + if (ch == '0') { + res ~= 0; + } + else if (ch == 'r') { + res ~= '\r'; + } + else if (ch == 'n') { + res ~= '\n'; + } + else if (ch == 't') { + res ~= '\t'; + } + else { + } + } + else { + res ~= cast(byte) ch; + } + lastBackSlash = false; + } + } + return res; } From 3a73559a2f4067ab6a1df40f0d0ea941e2fe73f7 Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Mon, 4 Jul 2016 17:07:11 +0800 Subject: [PATCH 10/30] fix bool value --- src/std/database/postgres/database.d | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/std/database/postgres/database.d b/src/std/database/postgres/database.d index 82f27dc..06560c1 100644 --- a/src/std/database/postgres/database.d +++ b/src/std/database/postgres/database.d @@ -622,14 +622,14 @@ struct Driver(Policy) { immutable char* ptr = cast(immutable char*) dt; auto str = cast(string) ptr[0 .. leng]; if (s == "true" || s == "t" || s == "1") - value = true; + value = 1; else if (s == "false" || s == "f" || s == "0") - value = false; + value = 0; else { auto t = cast(ubyte*) ptr; ubyte[int.sizeof] data; data[] = t[0 .. int.sizeof]; - value = (bigEndianToNative!int(data) > 0); + value = bigEndianToNative!int(data); } } break; From 5c60494b8eacadc9394c4dc2b8aa5f51e679da13 Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Mon, 4 Jul 2016 17:33:54 +0800 Subject: [PATCH 11/30] add sqlite type support --- src/std/database/sqlite/database.d | 45 ++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/src/std/database/sqlite/database.d b/src/std/database/sqlite/database.d index 616ad71..cea161a 100644 --- a/src/std/database/sqlite/database.d +++ b/src/std/database/sqlite/database.d @@ -213,7 +213,25 @@ struct Driver(Policy) { for (int i = 0; i < columns; ++i) { bind ~= Bind(); auto b = &bind.back(); - b.type = ValueType.String; + /* switch (sqlite3_column_type(st_, i)) + { + case SQLITE_INTEGER: + b.type = ValueType.Int; + break; + case SQLITE_FLOAT: + b.type = ValueType.Float; + break; + case SQLITE3_TEXT: { + b.type = ValueType.String; + break; + case SQLITE_BLOB: + b.type = ValueType.Raw; + break; + default: + b.type = ValueType.String; + break; + }*/ + b.type = ValueType.String; b.idx = i; } } @@ -244,7 +262,9 @@ struct Driver(Policy) { } ubyte[] rawData(Cell* cell) { - return null; //TODO: fix + ubyte * bytes = cast(ubyte *)sqlite3_column_blob(rs, idx); + int len = sqlite3_column_bytes(rs, idx); + return bytes[0..len]; } Variant getValue(Cell* cell) { @@ -252,25 +272,34 @@ struct Driver(Policy) { int idx = cast(int) cell.bind.idx; switch (sqlite3_column_type(st_, idx)) { case SQLITE_INTEGER: - value = sqlite3_column_int(st_, idx); + value = sqlite3_column_int64(st_, idx); + cell.bind.type = ValueType.Long; break; case SQLITE_FLOAT: value = sqlite3_column_double(st_, idx); + cell.bind.type = ValueType.Double; break; case SQLITE3_TEXT: { import core.stdc.string : strlen; - auto ptr = cast(immutable char*) sqlite3_column_text(st_, - cast(int) cell.bind.idx); - value = cast(string) ptr[0 .. strlen(ptr)]; // fix with length + auto ptr = cast(immutable char*) sqlite3_column_text(st_,idx); + int len = sqlite3_column_bytes(rs, idx); + value = cast(string) ptr[0 .. len]; // fix with length + cell.bind.type = ValueType.String; } break; - case SQLITE_BLOB: + case SQLITE_BLOB: { + ubyte * bytes = cast(ubyte *)sqlite3_column_blob(rs, idx); + int len = sqlite3_column_bytes(rs, idx); + value = bytes[0..len]; + cell.bind.type = ValueType.Raw; + } break; case SQLITE_NULL: + cell.bind.type = ValueType.Variant; break; } - return value; //TODO: + return value; } bool isNull(Cell* cell) { From 4b54dca5d4c449566a1a25c11c8309cdd2baca33 Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Mon, 4 Jul 2016 18:31:11 +0800 Subject: [PATCH 12/30] add mysql bold --- src/std/database/mysql/database.d | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/std/database/mysql/database.d b/src/std/database/mysql/database.d index 36990ec..6435cca 100644 --- a/src/std/database/mysql/database.d +++ b/src/std/database/mysql/database.d @@ -863,6 +863,10 @@ private struct Driver(Policy) { b.type = ValueType.DateTime; b.allocSize += 30; break; + case MYSQL_TYPE_BLOB: + b.type = ValueType.Raw; + b.allocSize += 512; + break; default: b.mysql_type = MYSQL_TYPE_STRING; b.type = ValueType.String; @@ -950,6 +954,11 @@ private struct Driver(Policy) { t.second); } break; + case ValueType.Raw:{ + auto ptr = cast(ubyte * ) cell.bind.data.ptr; + value = ptr[0 .. cell.bind.length]; + } + break; default: break; } From a17a021d09a4e697175a6c7abe463462d570e574 Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Tue, 5 Jul 2016 14:08:34 +0800 Subject: [PATCH 13/30] fix Converter --- src/std/database/front.d | 491 ++++++++++++++++++++++++----- src/std/database/sqlite/database.d | 2 +- 2 files changed, 407 insertions(+), 86 deletions(-) diff --git a/src/std/database/front.d b/src/std/database/front.d index d1aa62f..73d2ec4 100644 --- a/src/std/database/front.d +++ b/src/std/database/front.d @@ -40,6 +40,34 @@ struct Time { uint minute; uint second; uint msecond; + + string toString(){ + import std.string; + import std.format; + return format("%d:%d:%d.%d",hour,minute,second,msecond); + } + + static Time formString(string str){ + import std.string; + import std.array; + import std.conv; + int idx = cast(int)str.indexOf("."); + string dt; + uint msec = 0; + if(idx > 0) + { + dt = str[0..idx]; + msec = to!uint(str[idx..$]); + } + else + { + dt = str; + } + string[] tm = dt.split(":"); + if(tm.length != 3) + throw new Exception("erro string To Time : ", str); + return Time(to!uint(tm[0]),to!uint(tm[1]),to!uint(tm[2]),msec); + } } enum ValueType { @@ -60,16 +88,47 @@ enum ValueType { Raw, - Variant //TODO: remove +// Variant //TODO: remove } - // improve +struct TypeInfo(T : char) +{ + static auto type() { + return ValueType.Char; + } +} + +struct TypeInfo(T : short) +{ + static auto type() { + return ValueType.Short; + } +} + struct TypeInfo(T : int) { static auto type() { return ValueType.Int; } } +struct TypeInfo(T : long) { + static auto type() { + return ValueType.Long; + } +} + +struct TypeInfo(T : float) { + static auto type() { + return ValueType.Float; + } +} + +struct TypeInfo(T : double) { + static auto type() { + return ValueType.Double; + } +} + struct TypeInfo(T : string) { static auto type() { return ValueType.String; @@ -82,11 +141,29 @@ struct TypeInfo(T : Date) { } } -struct TypeInfo(T : Variant) { +struct TypeInfo(T : Time) { + static auto type() { + return ValueType.Time; + } +} + +struct TypeInfo(T : DateTime) { + static auto type() { + return ValueType.DateTime; + } +} + +struct TypeInfo(T : ubyte[]) { + static auto type() { + return ValueType.Raw; + } +} + +/*struct TypeInfo(T : Variant) { static auto type() { return ValueType.Variant; } -} +}*/ enum Feature { InputBinding, @@ -700,7 +777,7 @@ struct BasicValue(D, P) { } auto as(T)() { - return data_.coerce!T(); + return Converter.convert!T(cell_,this); } auto as(T : Variant)() { @@ -766,84 +843,328 @@ struct BasicCell(D, P) { } } -//Converter now is not used, but if del it ,it will build error. -struct Converter(D, P) { - alias Driver = D; - alias Policy = P; - alias Result = Driver.Result; - // alias Bind = Driver.Bind; - alias Cell = BasicCell!(Driver, Policy); - - // static Y convert(Y)(Result *r, ref Cell cell) { - /* ValueType x = cell.bind.type, y = TypeInfo!Y.type; - if (x == y) return r.get!Y(&cell); // temporary - auto e = lookup(x,y); - if (!e) conversionError(x,y); - Y value; - e.convert(r, &cell, &value);*/ - // return Y.init; - // } - - // static Y convert(Y:Variant)(Result *r, ref Cell cell) { - //return Y(123); - /* ValueType x = cell.bind.type, y = ValueType.Variant; - auto e = lookup(x,y); - if (!e) conversionError(x,y); - Y value; - e.convert(r, &cell, &value);*/ - // return Variant(); - // } - - // static Y convertDirect(Y)(Result *r, ref Cell cell) { - // assert(b.type == TypeInfo!Y.type); - // return Y.init;//return r.get!Y(&cell); - // } - - // private: - - struct Elem { - ValueType from, to; - void function(Result*, void*, void*) convert; - } - - // only cross converters, todo: all converters - static Elem[1] converters = [// {from: ValueType.Int, to: ValueType.String, &generate!(int,string).convert}, - // {from: ValueType.String, to: ValueType.Int, &generate!(string,int).convert}, - //{from: ValueType.Date, to: ValueType.String, &generate!(Date,string).convert}, - // variants - { - from: - ValueType.Int, to : ValueType.Variant, &generate!(int, Variant).convert},//{from: ValueType.String, to: ValueType.Variant, &generate!(string,Variant).convert}, - //{from: ValueType.Date, to: ValueType.Variant, &generate!(Date,Variant).convert}, - ]; - - // static Elem* lookup(ValueType x, ValueType y) { - // rework into efficient array lookup - // foreach(ref i; converters) { - // if (i.from == x && i.to == y) return &i; - // } - // return null; - // } - - struct generate(X, Y) { - static void convert(Result* r, void* x_, void* y_) { - // import std.conv; - Cell* cell = cast(Cell*) x_; - // *cast(Y*) y_ = to!Y(r.get!X(cell)); - } - } - - // struct generate(X,Y:Variant) { - // static void convert(Result *r, void *x_, void *y_) { - // Cell* cell = cast(Cell*) x_; - // *cast(Y*) y_ = r.get!X(cell); - // } - // } +/* +struct EfficientValue(T) { + alias Driver = T.Driver; + alias Bind = Driver.Bind; + private Bind* bind_; + alias Converter = .Converter!Driver; + + private Variant data_; + + this(Bind* bind) {bind_ = bind;} + + auto as(T:int)() {return Converter.convertDirect!T(bind_);} + auto as(T:string)() {return Converter.convertDirect!T(bind_);} + auto as(T:Date)() {return Converter.convertDirect!T(bind_);} + auto as(T:Variant)() {return Converter.convertDirect!T(bind_);} + +}*/ - // static void conversionError(ValueType x, ValueType y) { - // import std.conv; - // string msg; - // msg ~= "unsupported conversion from: " ~ to!string(x) ~ " to " ~ to!string(y); - // throw new DatabaseException(msg); - // } - } +import std.traits; +//Converter now is not used, but if del it ,it will build error. +struct Converter(D,P) { + alias Driver = D; + alias Policy = P; + //alias Result = Driver.Result; + alias Bind = Driver.Bind; + alias Cell = BasicCell!(Driver,Policy); + alias Value = Cell.Value; + + static Y convert(Y)(ref Cell cell,ref Value value) { + ValueType x = cell.bind.type, y = TypeInfo!Y.type; + if (x == y) return value.data_.get!Y(); + auto e = lookup(x,y); + if (!e) conversionError(x,y); + Y val; + e.convert(&(value.data_),&cell, &val); + return val; + } + + private: + + struct Elem { + ValueType from,to; + void function(Variant *,void *,void*) convert; + } + + // only cross converters, todo: all converters + static Elem[74] converters = [ + //to string + {from: ValueType.Char, to: ValueType.String, &generate!(char,string).convert}, + {from: ValueType.Short, to: ValueType.String, &generate!(short,string).convert}, + {from: ValueType.Int, to: ValueType.String, &generate!(int,string).convert}, + {from: ValueType.Long, to: ValueType.String, &generate!(int,string).convert}, + {from: ValueType.Float, to: ValueType.String, &generate!(float,string).convert}, + {from: ValueType.Double, to: ValueType.String, &generate!(double,string).convert}, + {from: ValueType.Raw, to: ValueType.String, &generate!(ubyte[],string).convert}, + {from: ValueType.Time, to: ValueType.String, &generate!(Time,string).convert}, + {from: ValueType.Date, to: ValueType.String, &generate!(Date,string).convert}, + {from: ValueType.DateTime, to: ValueType.String, &generate!(DateTime,string).convert}, + // string to other + {from: ValueType.String, to: ValueType.Char, &generate!(string,char).convert}, + {from: ValueType.String, to: ValueType.Short, &generate!(string,short).convert}, + {from: ValueType.String, to: ValueType.Int, &generate!(string,int).convert}, + {from: ValueType.String, to: ValueType.Long, &generate!(string,long).convert}, + {from: ValueType.String, to: ValueType.Float, &generate!(string,float).convert}, + {from: ValueType.String, to: ValueType.Double, &generate!(string,double).convert}, + {from: ValueType.String, to: ValueType.Raw, &generate!(string,ubyte[]).convert}, + {from: ValueType.String, to: ValueType.Time, &generate!(string,Time).convert}, + {from: ValueType.String, to: ValueType.Date, &generate!(string,Date).convert}, + {from: ValueType.String, to: ValueType.DateTime, &generate!(string,DateTime).convert}, + // char to other + {from: ValueType.Char, to: ValueType.Short, &generate!(char,short).convert}, + {from: ValueType.Char, to: ValueType.Int, &generate!(char,int).convert}, + {from: ValueType.Char, to: ValueType.Long, &generate!(char,long).convert}, + {from: ValueType.Char, to: ValueType.Float, &generate!(char,float).convert}, + {from: ValueType.Char, to: ValueType.Double, &generate!(char,double).convert}, + {from: ValueType.Char, to: ValueType.Raw, &generate!(char,ubyte[]).convert}, + //short to other + {from: ValueType.Short, to: ValueType.Char, &generate!(short,char).convert}, + {from: ValueType.Short, to: ValueType.Int, &generate!(short,int).convert}, + {from: ValueType.Short, to: ValueType.Long, &generate!(short,long).convert}, + {from: ValueType.Short, to: ValueType.Float, &generate!(short,float).convert}, + {from: ValueType.Short, to: ValueType.Double, &generate!(short,double).convert}, + {from: ValueType.Short, to: ValueType.Raw, &generate!(short,ubyte[]).convert}, + // int to other + {from: ValueType.Int, to: ValueType.Char, &generate!(int,char).convert}, + {from: ValueType.Int, to: ValueType.Short, &generate!(int,short).convert}, + {from: ValueType.Int, to: ValueType.Long, &generate!(int,long).convert}, + {from: ValueType.Int, to: ValueType.Float, &generate!(int,float).convert}, + {from: ValueType.Int, to: ValueType.Double, &generate!(int,double).convert}, + {from: ValueType.Int, to: ValueType.Raw, &generate!(int,ubyte[]).convert}, + // long to Other + {from: ValueType.Long, to: ValueType.Char, &generate!(long,char).convert}, + {from: ValueType.Long, to: ValueType.Short, &generate!(long,short).convert}, + {from: ValueType.Long, to: ValueType.Int, &generate!(long,int).convert}, + {from: ValueType.Long, to: ValueType.Float, &generate!(long,float).convert}, + {from: ValueType.Long, to: ValueType.Double, &generate!(long,double).convert}, + {from: ValueType.Long, to: ValueType.Raw, &generate!(long,ubyte[]).convert}, + {from: ValueType.Long, to: ValueType.DateTime, &generate!(int,DateTime).convert}, + //double to Other + {from: ValueType.Double, to: ValueType.Char, &generate!(double,char).convert}, + {from: ValueType.Double, to: ValueType.Short, &generate!(double,short).convert}, + {from: ValueType.Double, to: ValueType.Int, &generate!(double,int).convert}, + {from: ValueType.Double, to: ValueType.Float, &generate!(double,float).convert}, + {from: ValueType.Double, to: ValueType.Long, &generate!(double,long).convert}, + {from: ValueType.Double, to: ValueType.Raw, &generate!(double,ubyte[]).convert}, + //float to Other + {from: ValueType.Float, to: ValueType.Char, &generate!(float,char).convert}, + {from: ValueType.Float, to: ValueType.Short, &generate!(float,short).convert}, + {from: ValueType.Float, to: ValueType.Int, &generate!(float,int).convert}, + {from: ValueType.Float, to: ValueType.Double, &generate!(float,double).convert}, + {from: ValueType.Float, to: ValueType.Long, &generate!(float,long).convert}, + {from: ValueType.Float, to: ValueType.Raw, &generate!(float,ubyte[]).convert}, + //raw to other + {from: ValueType.Raw, to: ValueType.Char, &generate!(ubyte[],char).convert}, + {from: ValueType.Raw, to: ValueType.Short, &generate!(ubyte[],short).convert}, + {from: ValueType.Raw, to: ValueType.Int, &generate!(ubyte[],int).convert}, + {from: ValueType.Raw, to: ValueType.Long, &generate!(ubyte[],long).convert}, + {from: ValueType.Raw, to: ValueType.Float, &generate!(ubyte[],float).convert}, + {from: ValueType.Raw, to: ValueType.Double, &generate!(ubyte[],double).convert}, + {from: ValueType.Raw, to: ValueType.Time, &generate!(ubyte[],Time).convert}, + {from: ValueType.Raw, to: ValueType.Date, &generate!(ubyte[],Date).convert}, + {from: ValueType.Raw, to: ValueType.DateTime, &generate!(ubyte[],DateTime).convert}, + //date to datetime + {from: ValueType.Date, to: ValueType.DateTime, &generate!(Date,DateTime).convert}, + {from: ValueType.Date, to: ValueType.Raw, &generate!(Date,ubyte[]).convert}, + {from: ValueType.Time, to: ValueType.Raw, &generate!(Time,ubyte[]).convert}, + {from: ValueType.Time, to: ValueType.DateTime, &generate!(Time,DateTime).convert}, + // datetime to other + {from: ValueType.DateTime, to: ValueType.Date, &generate!(DateTime,Date).convert}, + {from: ValueType.DateTime, to: ValueType.Time, &generate!(DateTime,Time).convert}, + {from: ValueType.DateTime, to: ValueType.Long, &generate!(DateTime,long).convert}, + {from: ValueType.DateTime, to: ValueType.Raw, &generate!(DateTime,ubyte[]).convert} + ]; + + static Elem* lookup(ValueType x, ValueType y) { + // rework into efficient array lookup + foreach(ref i; converters) { + if (i.from == x && i.to == y) return &i; + } + return null; + } + + struct generate(X,Y) { + static void convert(Variant * v_,void *x_, void *y_) { + import std.conv; + Cell* cell = cast(Cell*) x_;// why this must be have? + *cast(Y*) y_ = to!Y(v_.get!X()); + } + } + + struct generate(X : DateTime,Y : Time) { + static void convert(Variant * v_,void *x_, void *y_) { + Cell* cell = cast(Cell*) x_;// why this must be have? + DateTime dt = v_.get!X(); + *cast(Y*) y_ = Time(dt.hour, dt.minute, dt.second); + } + } + + struct generate(X : DateTime,Y : long) { + static void convert(Variant * v_,void *x_, void *y_) { + Cell* cell = cast(Cell*) x_;// why this must be have? + DateTime dt = v_.get!X(); + *cast(Y*) y_ = SysTime(dt).toUnixTime() ; + } + } + + struct generate(X : DateTime,Y : Date) { + static void convert(Variant * v_,void *x_, void *y_) { + Cell* cell = cast(Cell*) x_;// why this must be have? + DateTime dt = v_.get!X(); + *cast(Y*) y_ = dt.date; + } + } + + struct generate(X : string,Y : Date) { + static void convert(Variant * v_,void *x_, void *y_) { + Cell* cell = cast(Cell*) x_;// why this must be have? + Date dt; + dt.fromISOExtString(v_.get!X()); + *cast(Y*) y_ = dt; + } + } + + struct generate(X : string,Y : Time) { + static void convert(Variant * v_,void *x_, void *y_) { + Cell* cell = cast(Cell*) x_;// why this must be have? + *cast(Y*) y_ = Time.formString(v_.get!X()); + } + } + + struct generate(X,Y : DateTime) + { + static void convert(Variant * v_,void *x_, void *y_) { + Cell* cell = cast(Cell*) x_;// why this must be have? + static if(is (X == ubyte[])) + { + ubyte[] dt = v_.get!X(); + if(dt.length < Y.sizeof) + throw new Exception("Raw Data Length is to smail!"); + auto data = dt[0..Y.sizeof]; + auto tm = cast(Y *) data.ptr; + *cast(Y*) y_ = *tm; + } + else static if(is(X == string)) + { + DateTime dt; + dt.fromISOExtString(v_.get!X()); + *cast(Y*) y_ = dt; + } + else static if (is(X == long)) + { + *cast(Y*) y_ = cast(DateTime)SysTime.fromUnixTime(v_.get!X()); + } + else + { + string msg = "unsupported conversion from: " ~ X.stringof ~ " to " ~ Y.stringof; + throw new DatabaseException(msg); + } + } + } + + struct generate(X : char, Y) + { + static void convert(Variant * v_,void *x_, void *y_) { + Cell* cell = cast(Cell*) x_;// why this must be have? + static if(is (Y == ubyte[])) + { + char a = v_.get!X(); + ubyte[] dt = new ubyte[1]; + dt[0] = cast(ubyte)a; + *cast(Y*) y_ = dt; + } + else static if(is(Y == string)) + { + string a; + a ~= v_.get!X(); + *cast(Y*) y_ = a; + } + else static if(isNumeric!Y) + { + *cast(Y*) y_ = v_.get!X(); + } + else + { + string msg = "unsupported conversion from: " ~ X.stringof ~ " to " ~ Y.stringof; + throw new DatabaseException(msg); + } + } + } + + struct generate(X , Y : char) + { + static void convert(Variant * v_,void *x_, void *y_) { + Cell* cell = cast(Cell*) x_;// why this must be have? + static if(is (X == ubyte[])) + { + ubyte[] dt = v_.get!X(); + *cast(Y*) y_ = cast(char)dt[0]; + } + else static if(is(X == string)) + { + string a = v_.get!X(); + *cast(Y*) y_ = a[0]; + } + else static if(isNumeric!Y) + { + *cast(Y*) y_ = cast(char)v_.get!X(); + } + else + { + string msg = "unsupported conversion from: " ~ X.stringof ~ " to " ~ Y.stringof; + throw new DatabaseException(msg); + } + } + } + + struct generate(X : ubyte[], Y) if(!(is(Y == char) || is(Y == DateTime))) + { + static void convert(Variant * v_,void *x_, void *y_) { + Cell* cell = cast(Cell*) x_;// why this must be have? + ubyte[] a = v_.get!X(); + static if(is(X == string)) + { + *cast(Y*) y_ = cast(string)a; + } + else + { + if(a.length < Y.sizeof) + throw new Exception("Raw Data Length is to smail!"); + auto data = a[0..Y.sizeof]; + auto tm = cast(Y *) data.ptr; + *cast(Y*) y_ = *tm; + } + } + } + + struct generate(X , Y : ubyte[]) if(!is(X == char)) + { + static void convert(Variant * v_,void *x_, void *y_) { + Cell* cell = cast(Cell*) x_;// why this must be have? + static if(is(X == string)) + { + string a = v_.get!X(); + *cast(Y*) y_ = cast(ubyte[])a; + } + else + { + ubyte[] data = new ubyte[X.sizeof]; + auto dt = v_.get!X(); + ubyte * ptr = cast(ubyte *) &dt; + data[] = ptr[0..X.sizeof]; + *cast(Y*) y_ = data; + } + } + } + + + + static void conversionError(ValueType x, ValueType y) { + import std.conv; + string msg; + msg ~= "unsupported conversion from: " ~ to!string(x) ~ " to " ~ to!string(y); + throw new DatabaseException(msg); + } +} \ No newline at end of file diff --git a/src/std/database/sqlite/database.d b/src/std/database/sqlite/database.d index cea161a..cd58254 100644 --- a/src/std/database/sqlite/database.d +++ b/src/std/database/sqlite/database.d @@ -296,7 +296,7 @@ struct Driver(Policy) { } break; case SQLITE_NULL: - cell.bind.type = ValueType.Variant; + cell.bind.type = ValueType.String; break; } return value; From 0c0c05f23b60bd99d4be764315a1be0d0cc3f7b4 Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Tue, 5 Jul 2016 15:46:30 +0800 Subject: [PATCH 14/30] rm ValueType.Variant --- src/std/database/front.d | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/std/database/front.d b/src/std/database/front.d index 73d2ec4..0b54735 100644 --- a/src/std/database/front.d +++ b/src/std/database/front.d @@ -86,9 +86,7 @@ enum ValueType { Time, DateTime, - Raw, - -// Variant //TODO: remove + Raw } // improve struct TypeInfo(T : char) From bab61ff63273313d94157de67bfa8e2a2386bd43 Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Tue, 5 Jul 2016 16:20:21 +0800 Subject: [PATCH 15/30] mysql and sqlite unittest --- mysql/test.d | 2 -- src/std/database/front.d | 4 ++++ src/std/database/poly/database.d | 2 +- src/std/database/sqlite/database.d | 13 ++++++++----- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/mysql/test.d b/mysql/test.d index d644ca4..41ac1fe 100644 --- a/mysql/test.d +++ b/mysql/test.d @@ -4,8 +4,6 @@ import std.database.mysql; import std.experimental.logger; import std.stdio; -import std.database.rowset; - unittest { import std.database.testsuite; alias DB = Database!DefaultPolicy; diff --git a/src/std/database/front.d b/src/std/database/front.d index 0b54735..5fe03f5 100644 --- a/src/std/database/front.d +++ b/src/std/database/front.d @@ -790,6 +790,10 @@ struct BasicValue(D, P) { return resultPtr.name(cell_.idx_); } + auto chars(){ + return as!string(); + } + auto get(T)() { if (data_.convertsTo!T()) return Nullable!T(as!T); diff --git a/src/std/database/poly/database.d b/src/std/database/poly/database.d index 5f5d8ec..acd68dc 100644 --- a/src/std/database/poly/database.d +++ b/src/std/database/poly/database.d @@ -441,7 +441,7 @@ struct Driver(Policy) { } Variant getValue(Cell* cell) { - return Variant; //TODO: + return Variant(); //TODO: } bool isNull(Cell* cell) { diff --git a/src/std/database/sqlite/database.d b/src/std/database/sqlite/database.d index cd58254..29f6674 100644 --- a/src/std/database/sqlite/database.d +++ b/src/std/database/sqlite/database.d @@ -262,8 +262,9 @@ struct Driver(Policy) { } ubyte[] rawData(Cell* cell) { - ubyte * bytes = cast(ubyte *)sqlite3_column_blob(rs, idx); - int len = sqlite3_column_bytes(rs, idx); + int idx = cast(int) cell.bind.idx; + ubyte * bytes = cast(ubyte *)sqlite3_column_blob(st_, idx); + int len = sqlite3_column_bytes(st_, idx); return bytes[0..len]; } @@ -283,14 +284,14 @@ struct Driver(Policy) { import core.stdc.string : strlen; auto ptr = cast(immutable char*) sqlite3_column_text(st_,idx); - int len = sqlite3_column_bytes(rs, idx); + int len = sqlite3_column_bytes(st_, idx); value = cast(string) ptr[0 .. len]; // fix with length cell.bind.type = ValueType.String; } break; case SQLITE_BLOB: { - ubyte * bytes = cast(ubyte *)sqlite3_column_blob(rs, idx); - int len = sqlite3_column_bytes(rs, idx); + ubyte * bytes = cast(ubyte *)sqlite3_column_blob(st_, idx); + int len = sqlite3_column_bytes(st_, idx); value = bytes[0..len]; cell.bind.type = ValueType.Raw; } @@ -298,6 +299,8 @@ struct Driver(Policy) { case SQLITE_NULL: cell.bind.type = ValueType.String; break; + default: + break; } return value; } From 4b08d987cc3af9088c813e9f64a0d187cebf5e02 Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Tue, 5 Jul 2016 17:16:58 +0800 Subject: [PATCH 16/30] postgres unittest --- src/std/database/postgres/bindings.d | 159 ++++++++++++++++++++++----- src/std/database/postgres/database.d | 7 +- 2 files changed, 134 insertions(+), 32 deletions(-) diff --git a/src/std/database/postgres/bindings.d b/src/std/database/postgres/bindings.d index f6a11b5..48d3871 100644 --- a/src/std/database/postgres/bindings.d +++ b/src/std/database/postgres/bindings.d @@ -4,37 +4,104 @@ import core.stdc.config; extern(System) { // from server/catalog/pg_type.h - enum int BOOLOID = 16; - enum int BYTEAOI = 17; - enum int CHAROID = 18; - enum int NAMEOID = 19; - enum int INT8OID = 20; - enum int INT2OID = 21; - enum int INT2VECTOROID = 22; - enum int INT4OID = 23; - enum int REGPROCOID = 24; - enum int TEXTOID = 25; - enum int OIDOID = 26; - enum int TIDOID = 27; - enum int XIDOID = 28; - enum int CIDOID = 29; - enum int OIDVECTOROID = 30; - enum int VARCHAROID = 1043; - enum int DATEOID = 1082; - + enum BOOLOID = 16; + enum BYTEAOID = 17; + enum CHAROID = 18; + enum NAMEOID = 19; + enum INT8OID = 20; + enum INT2OID = 21; + enum INT2VECTOROID = 22; + enum INT4OID = 23; + enum REGPROCOID = 24; + enum TEXTOID = 25; + enum OIDOID = 26; + enum TIDOID = 27; + enum XIDOID = 28; + enum CIDOID = 29; + enum OIDVECTOROID = 30; + enum JSONOID = 114; + enum XMLOID = 142; + enum PGNODETREEOID = 194; + enum POINTOID = 600; + enum LSEGOID = 601; + enum PATHOID = 602; + enum BOXOID = 603; + enum POLYGONOID = 604; + enum LINEOID = 628; + enum FLOAT4OID = 700; + enum FLOAT8OID = 701; + enum ABSTIMEOID = 702; + enum RELTIMEOID = 703; + enum TINTERVALOID = 704; + enum UNKNOWNOID = 705; + enum CIRCLEOID = 718; + enum CASHOID = 790; + enum MACADDROID = 829; + enum INETOID = 869; + enum CIDROID = 650; + enum INT2ARRAYOID = 1005; + enum INT4ARRAYOID = 1007; + enum TEXTARRAYOID = 1009; + enum OIDARRAYOID = 1028; + enum FLOAT4ARRAYOID = 1021; + enum ACLITEMOID = 1033; + enum CSTRINGARRAYOID = 1263; + enum BPCHAROID = 1042; + enum VARCHAROID = 1043; + enum DATEOID = 1082; + enum TIMEOID = 1083; + enum TIMESTAMPOID = 1114; + enum TIMESTAMPTZOID = 1184; + enum INTERVALOID = 1186; + enum TIMETZOID = 1266; + enum BITOID = 1560; + enum VARBITOID = 1562; + enum NUMERICOID = 1700; + enum REFCURSOROID = 1790; + enum REGPROCEDUREOID = 2202; + enum REGOPEROID = 2203; + enum REGOPERATOROID = 2204; + enum REGCLASSOID = 2205; + enum REGTYPEOID = 2206; + enum REGTYPEARRAYOID = 2211; + enum UUIDOID = 2951; + enum LSNOID = 3220; + enum TSVECTOROID = 3614; + enum GTSVECTOROID = 3642; + enum TSQUERYOID = 3615; + enum REGCONFIGOID = 3734; + enum REGDICTIONARYOID = 3769; + enum JSONBOID = 3802; + enum INT4RANGEOID = 3904; + enum RECORDOID = 2249; + enum RECORDARRAYOID = 2287; + enum CSTRINGOID = 2275; + enum ANYOID = 2276; + enum ANYARRAYOID = 2277; + enum VOIDOID = 2278; + enum TRIGGEROID = 2279; + enum EVTTRIGGEROID = 3838; + enum LANGUAGE_HANDLEROID = 2280; + enum INTERNALOID = 2281; + enum OPAQUEOID = 2282; + enum ANYELEMENTOID = 2283; + enum ANYNONARRAYOID = 2776; + enum ANYENUMOID = 3500; + enum FDW_HANDLEROID = 3115; + enum ANYRANGEOID = 3831; enum PGRES_EMPTY_QUERY = 0; - enum int CONNECTION_OK = 0; - enum int PGRES_COMMAND_OK = 1; - enum int PGRES_TUPLES_OK = 2; - enum int PGRES_COPY_OUT = 3; - enum int PGRES_COPY_IN = 4; - enum int PGRES_BAD_RESPONSE = 5; - enum int PGRES_NONFATAL_ERROR = 6; - enum int PGRES_FATAL_ERROR = 7; - enum int PGRES_COPY_BOTH = 8; - enum int PGRES_SINGLE_TUPLE = 9; + enum CONNECTION_OK = 0; + enum PGRES_COMMAND_OK = 1; + enum PGRES_TUPLES_OK = 2; + enum PGRES_COPY_OUT = 3; + enum PGRES_COPY_IN = 4; + enum PGRES_BAD_RESPONSE = 5; + enum PGRES_NONFATAL_ERROR = 6; + enum PGRES_FATAL_ERROR = 7; + enum PGRES_COPY_BOTH = 8; + enum PGRES_SINGLE_TUPLE = 9; alias ExecStatusType=int; alias Oid=uint; @@ -120,7 +187,7 @@ extern(System) { // numeric - enum int DECSIZE = 30; + enum DECSIZE = 30; alias ubyte NumericDigit; @@ -201,5 +268,39 @@ extern(System) { PGnotify *PQnotifies(PGconn *conn); void PQfreemem(void *ptr); + + + enum TYPTYPE_BASE = 'b'; /* base type (ordinary scalar type) */ + enum TYPTYPE_COMPOSITE = 'c' ;/* composite (e.g., table's rowtype) */ + enum TYPTYPE_DOMAIN = 'd' ;/* domain over another type */ + enum TYPTYPE_ENUM ='e' ;/* enumerated type */ + enum TYPTYPE_PSEUDO = 'p'; /* pseudo-type */ + enum TYPTYPE_RANGE ='r'; /* range type */ + + enum TYPCATEGORY_INVALID ='\0'; /* not an allowed category */ + enum TYPCATEGORY_ARRAY ='A'; + enum TYPCATEGORY_BOOLEAN ='B'; + enum TYPCATEGORY_COMPOSITE= 'C'; + enum TYPCATEGORY_DATETIME ='D'; + enum TYPCATEGORY_ENUM ='E'; + enum TYPCATEGORY_GEOMETRIC= 'G'; + enum TYPCATEGORY_NETWORK ='I' ;/* think INET */ + enum TYPCATEGORY_NUMERIC ='N'; + enum TYPCATEGORY_PSEUDOTYPE ='P'; + enum TYPCATEGORY_RANGE ='R'; + enum TYPCATEGORY_STRING = 'S'; + enum TYPCATEGORY_TIMESPAN= 'T'; + enum TYPCATEGORY_USER ='U'; + enum TYPCATEGORY_BITSTRING= 'V' ; /* er ... "varbit"? */ + enum TYPCATEGORY_UNKNOWN ='X'; + + bool IsPolymorphicType(int typid) + { + return ((typid) == ANYELEMENTOID || + (typid) == ANYARRAYOID || + (typid) == ANYNONARRAYOID || + (typid) == ANYENUMOID || + (typid) == ANYRANGEOID); + } } diff --git a/src/std/database/postgres/database.d b/src/std/database/postgres/database.d index 06560c1..1219974 100644 --- a/src/std/database/postgres/database.d +++ b/src/std/database/postgres/database.d @@ -333,7 +333,8 @@ struct Driver(Policy) { int fmt; string name; } - + // use std.database.front.ValueType not std.traits.ValueType + alias ValueType = std.database.front.ValueType; struct Bind { ValueType type; int idx; @@ -621,9 +622,9 @@ struct Driver(Policy) { case BOOLOID: { immutable char* ptr = cast(immutable char*) dt; auto str = cast(string) ptr[0 .. leng]; - if (s == "true" || s == "t" || s == "1") + if (str == "true" || str == "t" || str == "1") value = 1; - else if (s == "false" || s == "f" || s == "0") + else if (str == "false" || str == "f" || str == "0") value = 0; else { auto t = cast(ubyte*) ptr; From 485808fe2a708a277ccfc47bc9297439b5b80f9e Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Tue, 5 Jul 2016 17:25:22 +0800 Subject: [PATCH 17/30] add ValueType.UNKnown --- src/std/database/front.d | 6 +++++- src/std/database/postgres/database.d | 4 +++- src/std/database/sqlite/database.d | 4 +--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/std/database/front.d b/src/std/database/front.d index 5fe03f5..1eec64d 100644 --- a/src/std/database/front.d +++ b/src/std/database/front.d @@ -86,7 +86,9 @@ enum ValueType { Time, DateTime, - Raw + Raw, + + UNKnown } // improve struct TypeInfo(T : char) @@ -874,7 +876,9 @@ struct Converter(D,P) { alias Value = Cell.Value; static Y convert(Y)(ref Cell cell,ref Value value) { + if(value.isNull()) throw new DatabaseException("The Value is Null!"); ValueType x = cell.bind.type, y = TypeInfo!Y.type; + if(x == ValueType.UNKnown) throw new DatabaseException("The Value type is Unknown! please use rawData() get Data."); if (x == y) return value.data_.get!Y(); auto e = lookup(x,y); if (!e) conversionError(x,y); diff --git a/src/std/database/postgres/database.d b/src/std/database/postgres/database.d index 1219974..fc13f23 100644 --- a/src/std/database/postgres/database.d +++ b/src/std/database/postgres/database.d @@ -458,7 +458,9 @@ struct Driver(Policy) { b.type = ValueType.Raw; break; default: - throw new DatabaseException("unsupported type"); + b.type = ValueType.UNKnown; + break; + // throw new DatabaseException("unsupported type"); } } } diff --git a/src/std/database/sqlite/database.d b/src/std/database/sqlite/database.d index 29f6674..beed62a 100644 --- a/src/std/database/sqlite/database.d +++ b/src/std/database/sqlite/database.d @@ -295,11 +295,9 @@ struct Driver(Policy) { value = bytes[0..len]; cell.bind.type = ValueType.Raw; } - break; - case SQLITE_NULL: - cell.bind.type = ValueType.String; break; default: + cell.bind.type = ValueType.UNKnown; break; } return value; From 74c99ef5188f65563d906374348cd0808e1a4bcd Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Wed, 6 Jul 2016 10:22:51 +0800 Subject: [PATCH 18/30] add dbType to get the db type --- src/std/database/freetds/database.d | 4 ++++ src/std/database/front.d | 6 +++++- src/std/database/mysql/database.d | 8 ++++++++ src/std/database/odbc/database.d | 4 ++++ src/std/database/oracle/database.d | 4 ++++ src/std/database/poly/database.d | 2 ++ src/std/database/postgres/database.d | 2 +- src/std/database/sqlite/database.d | 5 +++++ 8 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/std/database/freetds/database.d b/src/std/database/freetds/database.d index 7736c4c..6731dbb 100644 --- a/src/std/database/freetds/database.d +++ b/src/std/database/freetds/database.d @@ -356,6 +356,10 @@ struct Driver(Policy) { return value; } + auto type(int col){ + return describe[i].type; + } + bool isNull(Cell* cell) { return false; } diff --git a/src/std/database/front.d b/src/std/database/front.d index 1eec64d..57dbdd9 100644 --- a/src/std/database/front.d +++ b/src/std/database/front.d @@ -786,7 +786,7 @@ struct BasicValue(D, P) { bool isNull() { return resultPtr.isNull(&cell_); - } //fix + } string name() { return resultPtr.name(cell_.idx_); @@ -803,6 +803,10 @@ struct BasicValue(D, P) { return Nullable!T; } + auto dbType(){ + resultPtr.type(cast(int)cell_.idx_); + } + // not sure if this does anything //const(char)[] chars() {return as!string;} diff --git a/src/std/database/mysql/database.d b/src/std/database/mysql/database.d index 6435cca..0559a83 100644 --- a/src/std/database/mysql/database.d +++ b/src/std/database/mysql/database.d @@ -509,6 +509,10 @@ private struct Driver(Policy) { return value; } + auto type(int col){ + return describe[col].field.type; + } + /* char[] get(X:char[])(Bind *b) { auto ptr = cast(char*) b.data.ptr; @@ -967,6 +971,10 @@ private struct Driver(Policy) { // value getters + auto type(int col){ + return describe[col].field.type; + } + auto name(size_t idx) { return describe[idx].name; } diff --git a/src/std/database/odbc/database.d b/src/std/database/odbc/database.d index d4a7694..c16e670 100644 --- a/src/std/database/odbc/database.d +++ b/src/std/database/odbc/database.d @@ -364,6 +364,10 @@ struct Driver(Policy) { } } + auto type(int col){ + return describe[i].type; + } + int fetch() { //info("SQLFetch"); status = SQLFetch(stmt.stmt); diff --git a/src/std/database/oracle/database.d b/src/std/database/oracle/database.d index e71522a..e88a8ed 100644 --- a/src/std/database/oracle/database.d +++ b/src/std/database/oracle/database.d @@ -573,6 +573,10 @@ struct Driver(Policy) { return value; //TODO: } + auto type(int col){ + return describe[i].oType; + } + bool isNull(Cell* cell) { return false; } diff --git a/src/std/database/poly/database.d b/src/std/database/poly/database.d index acd68dc..13901ea 100644 --- a/src/std/database/poly/database.d +++ b/src/std/database/poly/database.d @@ -436,6 +436,8 @@ struct Driver(Policy) { return 0; } + auto type(int col){return 0;} + ubyte[] rawData(Cell* cell) { return null; } diff --git a/src/std/database/postgres/database.d b/src/std/database/postgres/database.d index fc13f23..656c1b3 100644 --- a/src/std/database/postgres/database.d +++ b/src/std/database/postgres/database.d @@ -659,7 +659,7 @@ struct Driver(Policy) { return PQgetisnull(res, row, col) != 0; } - int type(int col) { + auto type(int col) { return describe[col].dbType; } diff --git a/src/std/database/sqlite/database.d b/src/std/database/sqlite/database.d index beed62a..385b16b 100644 --- a/src/std/database/sqlite/database.d +++ b/src/std/database/sqlite/database.d @@ -268,6 +268,11 @@ struct Driver(Policy) { return bytes[0..len]; } + auto type(int col) + { + return sqlite3_column_type(st_, col); + } + Variant getValue(Cell* cell) { Variant value; int idx = cast(int) cell.bind.idx; From 287c045904eed101f20d1e259c8d858c4276d949 Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Wed, 6 Jul 2016 16:41:19 +0800 Subject: [PATCH 19/30] mysql has field.name_length --- src/std/database/mysql/database.d | 2 +- src/std/database/poly/database.d | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/std/database/mysql/database.d b/src/std/database/mysql/database.d index 0559a83..a6edeb9 100644 --- a/src/std/database/mysql/database.d +++ b/src/std/database/mysql/database.d @@ -344,7 +344,7 @@ private struct Driver(Policy) { d.field = mysql_fetch_field(result_metadata); auto p = cast(immutable(char)*) d.field.name; - d.name = p[0 .. strlen(p)]; + d.name = p[0 .. d.field.name_length];//strlen(p)]; info("describe: name: ", d.name, ", mysql type: ", d.field.type); } diff --git a/src/std/database/poly/database.d b/src/std/database/poly/database.d index 13901ea..8a567fc 100644 --- a/src/std/database/poly/database.d +++ b/src/std/database/poly/database.d @@ -436,7 +436,7 @@ struct Driver(Policy) { return 0; } - auto type(int col){return 0;} + auto type(int col){return 0;} ubyte[] rawData(Cell* cell) { return null; From 01f69eecf6fbfa53dd80822650953b5fe87ae8ef Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Thu, 7 Jul 2016 10:12:04 +0800 Subject: [PATCH 20/30] add Value get Cell, and fix get(T)() --- src/std/database/front.d | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/std/database/front.d b/src/std/database/front.d index 57dbdd9..a2c43a4 100644 --- a/src/std/database/front.d +++ b/src/std/database/front.d @@ -763,6 +763,10 @@ struct BasicValue(D, P) { auto r = cell_.result_; return &(r.result()); // last parens matter here (something about delegate) } + + Cell cell(){ + return cell_; + } Variant value() { return data_; @@ -777,7 +781,7 @@ struct BasicValue(D, P) { } auto as(T)() { - return Converter.convert!T(cell_,this); + return Converter.convert!T(cell_,this); } auto as(T : Variant)() { @@ -792,20 +796,21 @@ struct BasicValue(D, P) { return resultPtr.name(cell_.idx_); } - auto chars(){ - return as!string(); - } + auto chars(){ + return as!string(); + } auto get(T)() { - if (data_.convertsTo!T()) + try{ return Nullable!T(as!T); - else + }catch{ return Nullable!T; + } } - auto dbType(){ - resultPtr.type(cast(int)cell_.idx_); - } + auto dbType(){ + resultPtr.type(cast(int)cell_.idx_); + } // not sure if this does anything //const(char)[] chars() {return as!string;} @@ -849,6 +854,10 @@ struct BasicCell(D, P) { auto rowIdx() { return rowIdx_; } + + auto columnIdx(){ + return idx_; + } } /* From 0ad85f4d121f7ca34edc455dbf0d9e66a1cd6026 Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Thu, 7 Jul 2016 14:54:08 +0800 Subject: [PATCH 21/30] fix dbtype() return value --- src/std/database/front.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/std/database/front.d b/src/std/database/front.d index a2c43a4..357741f 100644 --- a/src/std/database/front.d +++ b/src/std/database/front.d @@ -809,7 +809,7 @@ struct BasicValue(D, P) { } auto dbType(){ - resultPtr.type(cast(int)cell_.idx_); + return resultPtr.type(cast(int)cell_.idx_); } // not sure if this does anything From ed0daea1192534d0e56a4f8954f1bd6ffae7afb4 Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Thu, 7 Jul 2016 16:00:53 +0800 Subject: [PATCH 22/30] fix error --- src/std/database/front.d | 2 +- src/std/database/odbc/database.d | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/std/database/front.d b/src/std/database/front.d index 357741f..9239d42 100644 --- a/src/std/database/front.d +++ b/src/std/database/front.d @@ -804,7 +804,7 @@ struct BasicValue(D, P) { try{ return Nullable!T(as!T); }catch{ - return Nullable!T; + return Nullable!T(); } } diff --git a/src/std/database/odbc/database.d b/src/std/database/odbc/database.d index c16e670..c4107a9 100644 --- a/src/std/database/odbc/database.d +++ b/src/std/database/odbc/database.d @@ -394,7 +394,7 @@ struct Driver(Policy) { Variant getValue(Cell* cell) { Variant = value; - if (cell.bind.type = ValueType.String) { + if (cell.bind.type == ValueType.String) { auto ptr = cast(immutable char*) cell.bind.data; value = cast(string) ptr[0 .. cell.bind.len]; } From 8c2295d0b52484b7bb318630b15ac14404f7f9fa Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Thu, 7 Jul 2016 16:11:07 +0800 Subject: [PATCH 23/30] rm log in ~this() --- src/std/database/freetds/database.d | 2 +- src/std/database/mysql/database.d | 12 ++++++------ src/std/database/odbc/database.d | 6 +++--- src/std/database/oracle/database.d | 2 +- src/std/database/reference/database.d | 2 +- src/std/database/sqlite/database.d | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/std/database/freetds/database.d b/src/std/database/freetds/database.d index 6731dbb..782f3e3 100644 --- a/src/std/database/freetds/database.d +++ b/src/std/database/freetds/database.d @@ -69,7 +69,7 @@ struct Driver(Policy) { } ~this() { - info("~Database"); + // info("~Database"); //dbexit(); // should this be called (only on process exit?) } diff --git a/src/std/database/mysql/database.d b/src/std/database/mysql/database.d index a6edeb9..cfacb15 100644 --- a/src/std/database/mysql/database.d +++ b/src/std/database/mysql/database.d @@ -119,7 +119,7 @@ private struct Driver(Policy) { } ~this() { - log("~Database"); + // log("~Database"); } } @@ -140,7 +140,7 @@ private struct Driver(Policy) { } ~this() { - log("~Statement"); + // log("~Statement"); if (mysql) mysql_close(mysql); mysql = null; @@ -185,7 +185,7 @@ private struct Driver(Policy) { } ~this() { - log("~Statement"); + // log("~Statement"); foreach (b; inputBind) allocator.deallocate(b.data); if (stmt) @@ -321,12 +321,12 @@ private struct Driver(Policy) { } ~this() { - log("~Result"); + // log("~Result"); foreach (b; bind) allocator.deallocate(b.data); if (result_metadata) mysql_free_result(result_metadata); - log("~Result"); + // log("~Result"); } void build_describe() { @@ -612,7 +612,7 @@ private struct Driver(Policy) { } ~this() { - log("~Statement"); + // log("~Statement"); if (mysql) mysql_close(mysql); mysql = null; diff --git a/src/std/database/odbc/database.d b/src/std/database/odbc/database.d index c4107a9..5d2164d 100644 --- a/src/std/database/odbc/database.d +++ b/src/std/database/odbc/database.d @@ -58,7 +58,7 @@ struct Driver(Policy) { } ~this() { - info("~Database"); + // info("~Database"); if (!env) return; check("SQLFreeHandle", SQL_HANDLE_ENV, env, SQLFreeHandle(SQL_HANDLE_ENV, @@ -117,7 +117,7 @@ struct Driver(Policy) { } ~this() { - info("~Connection: ", source); + // info("~Connection: ", source); if (connected) check("SQLDisconnect()", SQL_HANDLE_DBC, con, SQLDisconnect(con)); check("SQLFreeHandle", SQLFreeHandle(SQL_HANDLE_DBC, con)); @@ -142,7 +142,7 @@ struct Driver(Policy) { } ~this() { - info("~Statement"); + // info("~Statement"); for (int i = 0; i < inputbind_.length; ++i) { allocator.deallocate(inputbind_[i].data[0 .. inputbind_[i].allocSize]); } diff --git a/src/std/database/oracle/database.d b/src/std/database/oracle/database.d index e88a8ed..ff5fbf5 100644 --- a/src/std/database/oracle/database.d +++ b/src/std/database/oracle/database.d @@ -179,7 +179,7 @@ struct Driver(Policy) { } ~this() { - info("oracle: closing database"); + // info("oracle: closing database"); if (env) { sword status = OCIHandleFree(env, OCI_HTYPE_ENV); env = null; diff --git a/src/std/database/reference/database.d b/src/std/database/reference/database.d index 47afa55..6d8ed19 100644 --- a/src/std/database/reference/database.d +++ b/src/std/database/reference/database.d @@ -61,7 +61,7 @@ struct Database(T) { } ~this() { - info("closing database resource"); + // info("closing database resource"); } this(this) { diff --git a/src/std/database/sqlite/database.d b/src/std/database/sqlite/database.d index 385b16b..5e4974e 100644 --- a/src/std/database/sqlite/database.d +++ b/src/std/database/sqlite/database.d @@ -72,7 +72,7 @@ struct Driver(Policy) { } ~this() { - writeln("sqlite closing ", path); + // writeln("sqlite closing ", path); if (sq) { int rc = sqlite3_close(sq); sq = null; From 2e441d51e05a4ef279e864bef0476e842dba3b41 Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Mon, 11 Jul 2016 14:58:14 +0800 Subject: [PATCH 24/30] add mysql error show --- src/std/database/mysql/database.d | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/std/database/mysql/database.d b/src/std/database/mysql/database.d index cfacb15..9b04179 100644 --- a/src/std/database/mysql/database.d +++ b/src/std/database/mysql/database.d @@ -73,8 +73,11 @@ private static void raiseError()(string msg, int ret) { private static void raiseError()(string msg, MYSQL_STMT* stmt, int ret) { import core.stdc.string : strlen; - + import std.string; + const(char*) err = mysql_stmt_error(stmt); + msg ~= " erro: "; + msg ~= fromStringz(err); throw new DatabaseException("mysql error: " ~ msg); } From 7057c98c10479e9c6cd50cb02f375b7731a4f606 Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Mon, 11 Jul 2016 15:15:21 +0800 Subject: [PATCH 25/30] fix style --- src/std/database/front.d | 60 ++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/std/database/front.d b/src/std/database/front.d index 9239d42..3aa4845 100644 --- a/src/std/database/front.d +++ b/src/std/database/front.d @@ -41,33 +41,33 @@ struct Time { uint second; uint msecond; - string toString(){ - import std.string; - import std.format; - return format("%d:%d:%d.%d",hour,minute,second,msecond); - } - - static Time formString(string str){ - import std.string; - import std.array; - import std.conv; - int idx = cast(int)str.indexOf("."); - string dt; - uint msec = 0; - if(idx > 0) - { - dt = str[0..idx]; - msec = to!uint(str[idx..$]); - } - else - { - dt = str; - } - string[] tm = dt.split(":"); - if(tm.length != 3) - throw new Exception("erro string To Time : ", str); - return Time(to!uint(tm[0]),to!uint(tm[1]),to!uint(tm[2]),msec); - } + string toString(){ + import std.string; + import std.format; + return format("%d:%d:%d.%d",hour,minute,second,msecond); + } + + static Time formString(string str){ + import std.string; + import std.array; + import std.conv; + int idx = cast(int)str.indexOf("."); + string dt; + uint msec = 0; + if(idx > 0) + { + dt = str[0..idx]; + msec = to!uint(str[idx..$]); + } + else + { + dt = str; + } + string[] tm = dt.split(":"); + if(tm.length != 3) + throw new Exception("erro string To Time : ", str); + return Time(to!uint(tm[0]),to!uint(tm[1]),to!uint(tm[2]),msec); + } } enum ValueType { @@ -88,7 +88,7 @@ enum ValueType { Raw, - UNKnown + UNKnown } // improve struct TypeInfo(T : char) @@ -786,7 +786,7 @@ struct BasicValue(D, P) { auto as(T : Variant)() { return data_; - } //Converter.convert!T(resultPtr, cell_);} + } bool isNull() { return resultPtr.isNull(&cell_); @@ -804,7 +804,7 @@ struct BasicValue(D, P) { try{ return Nullable!T(as!T); }catch{ - return Nullable!T(); + return Nullable!T.init; } } From 990295615e46dc698330975d6dd386d1e94d7c02 Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Tue, 12 Jul 2016 12:20:05 +0800 Subject: [PATCH 26/30] fix mysql max data and raw --- src/std/database/allocator.d | 6 +-- src/std/database/mysql/database.d | 75 +++++++++++++++---------------- 2 files changed, 40 insertions(+), 41 deletions(-) diff --git a/src/std/database/allocator.d b/src/std/database/allocator.d index e6c4497..bd9670e 100644 --- a/src/std/database/allocator.d +++ b/src/std/database/allocator.d @@ -10,7 +10,7 @@ struct MyMallocator { import core.stdc.stdlib : malloc; if (!bytes) return null; auto p = malloc(bytes); - //log("allocate: ptr: ", p , " size :",bytes); + //log("allocate: ptr: ", p , " size :",bytes); return p ? p[0 .. bytes] : null; } @@ -18,9 +18,9 @@ struct MyMallocator { @system // removed @nogc and nothrow for logging bool deallocate(void[] b) { import core.stdc.stdlib : free; - //log("deallocate: ptr: ", b.ptr, " size: ", b.length); + log("deallocate: ptr: ", b.ptr, " size: ", b.length); free(b.ptr); - //log("return true"); + //log("return true"); return true; } diff --git a/src/std/database/mysql/database.d b/src/std/database/mysql/database.d index 9b04179..9208be6 100644 --- a/src/std/database/mysql/database.d +++ b/src/std/database/mysql/database.d @@ -308,7 +308,7 @@ private struct Driver(Policy) { MYSQL_RES* result_metadata; int status; - static const maxData = 256; + static const maxData = 2048; this(Statement* stmt_, int rowArraySize_) { stmt = stmt_; @@ -359,12 +359,14 @@ private struct Driver(Policy) { bind.reserve(columns); + for (int i = 0; i != columns; ++i) { auto d = &describe[i]; bind ~= Bind(); auto b = &bind.back(); b.mysql_type = d.field.type; b.allocSize = cast(uint)(d.field.length + 1); + switch (d.field.type) { case MYSQL_TYPE_TINY: b.type = ValueType.Char; @@ -400,6 +402,10 @@ private struct Driver(Policy) { b.type = ValueType.DateTime; b.allocSize += 30; break; + case MYSQL_TYPE_BLOB: + b.type = ValueType.Raw; + b.allocSize += 512; + break; default: b.mysql_type = MYSQL_TYPE_STRING; b.type = ValueType.String; @@ -407,6 +413,7 @@ private struct Driver(Policy) { break; } + // trace("d.field.length : ", d.field.length, " type is : ", b.type); // let in ints for now /* if (d.field.type == MYSQL_TYPE_LONG) { b.mysql_type = d.field.type; @@ -454,36 +461,23 @@ private struct Driver(Policy) { if (isNull(cell)) return value; switch (cell.bind.type) { - case ValueType.Char: { - auto t = (*cast(char*) cell.bind.data.ptr); - value = t; - } + case ValueType.Char: + value = (*cast(char*) cell.bind.data.ptr); break; - case ValueType.Short: { - auto t = (*cast(short*) cell.bind.data.ptr); - value = t; - - } + case ValueType.Short: + value = (*cast(short*) cell.bind.data.ptr); break; - case ValueType.Int: { - auto t = (*cast(int*) cell.bind.data.ptr); - value = t; - } + case ValueType.Int: + value = (*cast(int*) cell.bind.data.ptr); break; - case ValueType.Long: { - auto t = (*cast(long*) cell.bind.data.ptr); - value = t; - } + case ValueType.Long: + value = (*cast(long*) cell.bind.data.ptr); break; - case ValueType.Float: { - auto t = (*cast(float*) cell.bind.data.ptr); - value = t; - } + case ValueType.Float: + value = (*cast(float*) cell.bind.data.ptr); break; - case ValueType.Double: { - auto t = (*cast(double*) cell.bind.data.ptr); - value = t; - } + case ValueType.Double: + value = (*cast(double*) cell.bind.data.ptr); break; case ValueType.String: { auto ptr = cast(char*) cell.bind.data.ptr; @@ -506,15 +500,20 @@ private struct Driver(Policy) { t.second); } break; + case ValueType.Raw:{ + auto ptr = cast(ubyte * ) cell.bind.data.ptr; + value = ptr[0 .. cell.bind.length]; + } + break; default: break; } return value; } - auto type(int col){ - return describe[col].field.type; - } + auto type(int col){ + return describe[col].field.type; + } /* char[] get(X:char[])(Bind *b) { @@ -870,10 +869,10 @@ private struct Driver(Policy) { b.type = ValueType.DateTime; b.allocSize += 30; break; - case MYSQL_TYPE_BLOB: - b.type = ValueType.Raw; - b.allocSize += 512; - break; + case MYSQL_TYPE_BLOB: + b.type = ValueType.Raw; + b.allocSize += 512; + break; default: b.mysql_type = MYSQL_TYPE_STRING; b.type = ValueType.String; @@ -961,11 +960,11 @@ private struct Driver(Policy) { t.second); } break; - case ValueType.Raw:{ - auto ptr = cast(ubyte * ) cell.bind.data.ptr; - value = ptr[0 .. cell.bind.length]; - } - break; + case ValueType.Raw:{ + auto ptr = cast(ubyte * ) cell.bind.data.ptr; + value = ptr[0 .. cell.bind.length]; + } + break; default: break; } From 4771d950383c04de4f74d31a1fd18a438d11fe5c Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Tue, 12 Jul 2016 12:39:53 +0800 Subject: [PATCH 27/30] fix date allo size litte --- src/std/database/allocator.d | 2 +- src/std/database/mysql/database.d | 19 +++++-------------- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/std/database/allocator.d b/src/std/database/allocator.d index bd9670e..8e0830e 100644 --- a/src/std/database/allocator.d +++ b/src/std/database/allocator.d @@ -18,7 +18,7 @@ struct MyMallocator { @system // removed @nogc and nothrow for logging bool deallocate(void[] b) { import core.stdc.stdlib : free; - log("deallocate: ptr: ", b.ptr, " size: ", b.length); + //log("deallocate: ptr: ", b.ptr, " size: ", b.length); free(b.ptr); //log("return true"); return true; diff --git a/src/std/database/mysql/database.d b/src/std/database/mysql/database.d index 9208be6..295832c 100644 --- a/src/std/database/mysql/database.d +++ b/src/std/database/mysql/database.d @@ -389,18 +389,19 @@ private struct Driver(Policy) { case MYSQL_TYPE_DATE: b.type = ValueType.Date; + b.allocSize += 32; break; case MYSQL_TYPE_TIME: case MYSQL_TYPE_TIME2: b.type = ValueType.Time; - b.allocSize += 30; + b.allocSize += 32; break; case MYSQL_TYPE_DATETIME2: case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_TIMESTAMP2: case MYSQL_TYPE_TIMESTAMP: b.type = ValueType.DateTime; - b.allocSize += 30; + b.allocSize += 32; break; case MYSQL_TYPE_BLOB: b.type = ValueType.Raw; @@ -413,7 +414,7 @@ private struct Driver(Policy) { break; } - // trace("d.field.length : ", d.field.length, " type is : ", b.type); + trace("d.field.length : ", d.field.length, " type is : ", b.type); // let in ints for now /* if (d.field.type == MYSQL_TYPE_LONG) { b.mysql_type = d.field.type; @@ -856,6 +857,7 @@ private struct Driver(Policy) { case MYSQL_TYPE_DATE: b.type = ValueType.Date; + b.allocSize += 32; break; case MYSQL_TYPE_TIME: case MYSQL_TYPE_TIME2: @@ -880,17 +882,6 @@ private struct Driver(Policy) { break; } - // let in ints for now - /* if (d.field.type == MYSQL_TYPE_LONG) { - b.mysql_type = d.field.type; - b.type = ValueType.Int; - } else if (d.field.type == MYSQL_TYPE_DATE) { - b.mysql_type = d.field.type; - b.type = ValueType.Date; - } else { - b.mysql_type = MYSQL_TYPE_STRING; - b.type = ValueType.String; - }*/ b.data = allocator.allocate(b.allocSize); } From 3092c36b0b7e22ceeb76c01b26d6fb5a7bb33a35 Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Tue, 12 Jul 2016 14:35:12 +0800 Subject: [PATCH 28/30] fix postgers connect and erro message --- src/std/database/postgres/database.d | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/std/database/postgres/database.d b/src/std/database/postgres/database.d index 656c1b3..78fd18b 100644 --- a/src/std/database/postgres/database.d +++ b/src/std/database/postgres/database.d @@ -17,6 +17,7 @@ import std.database.front; import std.stdio; import std.typecons; import std.datetime; +import std.conv; struct DefaultPolicy { alias Allocator = MyMallocator; @@ -43,14 +44,14 @@ void error()(PGconn* con, string msg) { import std.conv; auto s = msg ~ to!string(PQerrorMessage(con)); - throw new DatabaseException(msg); + throw new DatabaseException(s); } void error()(PGconn* con, string msg, int result) { import std.conv; auto s = "error:" ~ msg ~ ": " ~ to!string(result) ~ ": " ~ to!string(PQerrorMessage(con)); - throw new DatabaseException(msg); + throw new DatabaseException(s); } int check()(PGconn* con, string msg, int result) { @@ -98,7 +99,19 @@ struct Driver(Policy) { source = source_; string conninfo; - conninfo ~= "dbname=" ~ source.database; + if(source.host.length > 0) + { + conninfo ~= "host=" ~ source.host; + if(source.port > 0) conninfo ~= " port=" ~ to!string(source.port); + } + else + { + conninfo ~= "host=" ~ source.server; + } + conninfo ~= " dbname=" ~ source.database; + if(source.username.length > 0) conninfo ~= " user=" ~ source.username; + if(source.password.length > 0) conninfo ~= " password=" ~ source.password; + trace("link string is : ", conninfo); con = PQconnectdb(toStringz(conninfo)); if (PQstatus(con) != CONNECTION_OK) error(con, "login error"); From 7c07b6ac60b7dea86e89e66a7e378d099527658b Mon Sep 17 00:00:00 2001 From: dushibaiyu Date: Tue, 12 Jul 2016 16:17:26 +0800 Subject: [PATCH 29/30] postgresql fix --- src/std/database/postgres/bindings.d | 3 + src/std/database/postgres/database.d | 147 +++++++++++---------------- 2 files changed, 62 insertions(+), 88 deletions(-) diff --git a/src/std/database/postgres/bindings.d b/src/std/database/postgres/bindings.d index 48d3871..2c84c72 100644 --- a/src/std/database/postgres/bindings.d +++ b/src/std/database/postgres/bindings.d @@ -118,6 +118,7 @@ extern(System) { struct PGresult {}; int PQsendQuery(PGconn *conn, const char *command); + PGresult *PQgetResult(PGconn *conn); int PQsetSingleRowMode(PGconn *conn); @@ -125,6 +126,8 @@ extern(System) { ExecStatusType PQresultStatus(const PGresult *res); char *PQresStatus(ExecStatusType status); + PGresult * PQexec(PGconn *conn, const char *command); + PGresult *PQexecParams( PGconn *conn, const char *command, diff --git a/src/std/database/postgres/database.d b/src/std/database/postgres/database.d index 78fd18b..3650413 100644 --- a/src/std/database/postgres/database.d +++ b/src/std/database/postgres/database.d @@ -139,10 +139,10 @@ struct Driver(Policy) { PGresult* prepareRes; PGresult* res; - Array!(char*) bindValue; + /* Array!(char*) bindValue; Array!(Oid) bindType; Array!(int) bindLength; - Array!(int) bindFormat; + Array!(int) bindFormat;*/ this(Connection* connection_, string sql_) { connection = connection_; @@ -153,11 +153,11 @@ struct Driver(Policy) { } ~this() { - for (int i = 0; i != bindValue.length; ++i) { +/* for (int i = 0; i != bindValue.length; ++i) { auto ptr = bindValue[i]; auto length = bindLength[i]; allocator.deallocate(ptr[0 .. length]); - } + }*/ } void bind(int n, int value) { @@ -171,20 +171,11 @@ struct Driver(Policy) { info("query sql: ", sql); - if (!prepareRes) - prepare(); - auto n = bindValue.length; - int resultFormat = 1; - static if (Policy.nonblocking) { checkForZero(con, "PQsetnonblocking", PQsetnonblocking(con, 1)); - check(con, "PQsendQueryPrepared", PQsendQueryPrepared(con, - toStringz(name), cast(int) n, - n ? cast(const char**)&bindValue[0] : null, - n ? cast(int*)&bindLength[0] : null, - n ? cast(int*)&bindFormat[0] : null, resultFormat)); + check(con, "PQsendQueryPrepared", PQsendQuery(con,toStringz(sql))); do { Policy.Handler handler; @@ -214,20 +205,8 @@ struct Driver(Policy) { } else { - res = PQexecPrepared(con, toStringz(name), cast(int) n, - n ? cast(const char**)&bindValue[0] : null, - n ? cast(int*)&bindLength[0] : null, - n ? cast(int*)&bindFormat[0] : null, resultFormat); + res = PQexec(con,toStringz(sql)); } - - /* - not using for now - if (!PQsendQuery(con, toStringz(sql))) throw error("PQsendQuery"); - res = PQgetResult(con); - */ - - // problem with PQsetSingleRowMode and prepared statements - // if (!PQsetSingleRowMode(con)) throw error("PQsetSingleRowMode"); } void query(X...)(X args) { @@ -235,14 +214,14 @@ struct Driver(Policy) { // todo: stack allocation - bindValue.clear(); + /* bindValue.clear(); bindType.clear(); bindLength.clear(); bindFormat.clear(); foreach (ref arg; args) bind(arg); - + auto n = bindValue.length; /* @@ -257,19 +236,27 @@ struct Driver(Policy) { 0); */ - int resultForamt = 0; + /* int resultForamt = 0; res = PQexecParams(con, toStringz(sql), cast(int) n, n ? cast(Oid*)&bindType[0] : null, n ? cast(const char**)&bindValue[0] : null, n ? cast(int*)&bindLength[0] : null, n ? cast(int*)&bindFormat[0] : null, resultForamt); + */ } bool hasRows() { - return true; + return rowCout() > 0; } - + + int rowCout() + { + int r = 0; + if(res !is null) r = PQntuples(res); + return r; + } +/* int binds() { return cast(int) bindValue.length; } // fix @@ -300,7 +287,7 @@ struct Driver(Policy) { void bind(Date v) { /* utility functions take 8 byte values but DATEOID is a 4 byte value */ - import std.bitmanip; + /* import std.bitmanip; int[3] mdy; mdy[0] = v.month; @@ -315,7 +302,7 @@ struct Driver(Policy) { bindLength ~= cast(int) s.length; bindFormat ~= 1; } - +*/ void prepare() { const Oid* paramTypes; prepareRes = PQprepare(con, toStringz(name), toStringz(sql), 0, paramTypes); @@ -366,6 +353,7 @@ struct Driver(Policy) { int row; int rows; bool hasResult_; + bool fristFecth = true; // artifical bind array (for now) Array!Bind bind; @@ -479,7 +467,16 @@ struct Driver(Policy) { } int fetch() { - return ++row != rows ? 1 : 0; + int r = 0; + if(fristFecth) + r = rows > 0 ? 1 : 0; + else + { + ++row; + r = row != rows ? 1 : 0; + } + fristFecth = false; + return r; } bool singleRownext() { @@ -549,7 +546,7 @@ struct Driver(Policy) { } Variant getValue(Cell* cell) { - import std.bitmanip; + import std.conv; Variant value; if (isNull(cell)) @@ -557,101 +554,75 @@ struct Driver(Policy) { void* dt = data(cell.bind.idx); int leng = len(cell.bind.idx); + immutable char* ptr = cast(immutable char*) dt; + string str = cast(string) ptr[0 .. leng]; switch (type(cell.bind.idx)) { - case VARCHAROID: { - immutable char* ptr = cast(immutable char*) dt; - value = cast(string) ptr[0 .. leng]; + case VARCHAROID: + case TEXTOID: + case NAMEOID:{ + value = str; } break; case INT2OID: { - auto t = cast(ubyte*) dt; - ubyte[short.sizeof] data; - data[] = t[0 .. short.sizeof]; - value = bigEndianToNative!short(data); + value = parse!short(str); } break; case INT4OID: { - auto t = cast(ubyte*) dt; - ubyte[int.sizeof] data; - data[] = t[0 .. int.sizeof]; - value = bigEndianToNative!int(data); + value = parse!int(str); } break; case INT8OID: { - auto t = cast(ubyte*) dt; - ubyte[long.sizeof] data; - data[] = t[0 .. long.sizeof]; - value = bigEndianToNative!long(data); + value = parse!long(str); } break; case FLOAT4OID: { - auto t = cast(ubyte*) dt; - ubyte[float.sizeof] data; - data[] = t[0 .. float.sizeof]; - value = bigEndianToNative!float(data); + value = parse!float(str); } break; case FLOAT8OID: { - auto t = cast(ubyte*) dt; - ubyte[double.sizeof] data; - data[] = t[0 .. double.sizeof]; - value = bigEndianToNative!double(data); + value = parse!double(str); } break; case DATEOID: { - import std.bitmanip; - - auto ptr = cast(ubyte*) data(cell.bind.idx); - int sz = len(cell.bind.idx); - date d = bigEndianToNative!uint(ptr[0 .. 4]); // why not sz? - int[3] mdy; - PGTYPESdate_julmdy(d, &mdy[0]); - value = Date(mdy[2], mdy[0], mdy[1]); + import std.format: formattedRead; + string input = str; + int year, month, day; + formattedRead(input, "%s-%s-%s", &year, &month, &day); + value = Date(year, month, day); } break; case TIMESTAMPOID: { import std.string; - - immutable char* ptr = cast(immutable char*) dt; - auto str = cast(string) ptr[0 .. leng]; + value = DateTime.fromISOExtString(str.translate([' ' : 'T']).split('.').front()); } break; case TIMEOID: { - immutable char* ptr = cast(immutable char*) dt; - auto str = cast(string) ptr[0 .. leng]; value = parseTimeoid(str); } break; case BYTEAOID: { - immutable char* ptr = cast(immutable char*) dt; - auto str = cast(string) ptr[0 .. leng]; value = byteaToUbytes(str); } break; case CHAROID: { - auto t = cast(char*) dt; - value = cast(char)(leng > 0 ? t[0] : 0x00); + value = cast(char)(leng > 0 ? str[0] : 0x00); } break; case BOOLOID: { - immutable char* ptr = cast(immutable char*) dt; - auto str = cast(string) ptr[0 .. leng]; - if (str == "true" || str == "t" || str == "1") - value = 1; - else if (str == "false" || str == "f" || str == "0") - value = 0; - else { - auto t = cast(ubyte*) ptr; - ubyte[int.sizeof] data; - data[] = t[0 .. int.sizeof]; - value = bigEndianToNative!int(data); - } + + if (str == "true" || str == "t" || str == "1") + value = 1; + else if (str == "false" || str == "f" || str == "0") + value = 0; + else + value = parse!int(str); } break; default: break; } + trace("value is : ",value ); return value; } From 3f9a84adeddd1735c1a9a10802520fae11b360d2 Mon Sep 17 00:00:00 2001 From: Brian Zou Date: Tue, 12 Jul 2016 23:33:38 +0800 Subject: [PATCH 30/30] Update dub.json --- dub.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dub.json b/dub.json index e54a687..93c9896 100644 --- a/dub.json +++ b/dub.json @@ -1,8 +1,8 @@ { - "name": "dstddb", + "name": "database", "description": "A D standard database proposal and implementation", - "copyright": "Copyright © 2015, Erik Smith", - "authors": ["Erik Smith"], + "copyright": "Copyright (C) 2015-2016 Shanghai Putao Technology Co., Ltd", + "authors": ["dlang team of putao labs."], "license": "MIT", "sourcePaths": ["src"] }