diff --git a/cgi.d b/cgi.d index bb3e144..d136cca 100644 --- a/cgi.d +++ b/cgi.d @@ -6076,21 +6076,24 @@ void runAddonServer(EIS)(string localListenerName, EIS eis) if(is(EIS : EventIoS if(listen(sock, 128) == -1) throw new Exception("listen " ~ to!string(errno)); + makeNonBlocking(sock); + version(linux) { - - makeNonBlocking(sock); - import core.sys.linux.epoll; auto epoll_fd = epoll_create1(EPOLL_CLOEXEC); if(epoll_fd == -1) throw new Exception("epoll_create1 " ~ to!string(errno)); scope(failure) close(epoll_fd); + } else { + import core.sys.posix.poll; + } - auto acceptOp = allocateIoOp(sock, IoOp.Read, 0, null); - scope(exit) - freeIoOp(acceptOp); + auto acceptOp = allocateIoOp(sock, IoOp.Read, 0, null); + scope(exit) + freeIoOp(acceptOp); + version(linux) { epoll_event ev; ev.events = EPOLLIN | EPOLLET; ev.data.ptr = acceptOp; @@ -6098,111 +6101,153 @@ void runAddonServer(EIS)(string localListenerName, EIS eis) if(is(EIS : EventIoS throw new Exception("epoll_ctl " ~ to!string(errno)); epoll_event[64] events; + } else { + pollfd[] pollfds; + pollfds ~= pollfd(sock, POLLIN); + IoOp*[int] ioops; + } - while(true) { + while(true) { - // FIXME: it should actually do a timerfd that runs on any thing that hasn't been run recently + // FIXME: it should actually do a timerfd that runs on any thing that hasn't been run recently - int timeout_milliseconds = 15000; // -1; // infinite - //writeln("waiting for ", name); + int timeout_milliseconds = 15000; // -1; // infinite + //writeln("waiting for ", name); + + version(linux) { auto nfds = epoll_wait(epoll_fd, events.ptr, events.length, timeout_milliseconds); if(nfds == -1) { if(errno == EINTR) continue; throw new Exception("epoll_wait " ~ to!string(errno)); } + } else { + int nfds = poll(pollfds.ptr, pollfds.length, timeout_milliseconds); + } - if(nfds == 0) { - eis.wait_timeout(); - } + if(nfds == 0) { + eis.wait_timeout(); + } - foreach(idx; 0 .. nfds) { + foreach(idx; 0 .. nfds) { + version(linux) { auto flags = events[idx].events; auto ioop = cast(IoOp*) events[idx].data.ptr; + } else { + auto ioop = ioops[pollfds[idx].fd]; + } - //writeln(flags, " ", ioop.fd); + //writeln(flags, " ", ioop.fd); - if(ioop.fd == sock && (flags & EPOLLIN)) { - // on edge triggering, it is important that we get it all - while(true) { - auto size = cast(uint) addr.sizeof; - auto ns = accept(sock, cast(sockaddr*) &addr, &size); - if(ns == -1) { - if(errno == EAGAIN || errno == EWOULDBLOCK) { - // all done, got it all - break; - } - throw new Exception("accept " ~ to!string(errno)); + void newConnection() { + // on edge triggering, it is important that we get it all + while(true) { + auto size = cast(uint) addr.sizeof; + auto ns = accept(sock, cast(sockaddr*) &addr, &size); + if(ns == -1) { + if(errno == EAGAIN || errno == EWOULDBLOCK) { + // all done, got it all + break; } + throw new Exception("accept " ~ to!string(errno)); + } - makeNonBlocking(ns); + makeNonBlocking(ns); + auto niop = allocateIoOp(ns, IoOp.ReadSocketHandle, 4096, &eis.handleLocalConnectionData); + niop.closeHandler = &eis.handleLocalConnectionClose; + niop.completeHandler = &eis.handleLocalConnectionComplete; + scope(failure) freeIoOp(niop); + + version(linux) { epoll_event nev; nev.events = EPOLLIN | EPOLLET; - auto niop = allocateIoOp(ns, IoOp.ReadSocketHandle, 4096, &eis.handleLocalConnectionData); - niop.closeHandler = &eis.handleLocalConnectionClose; - niop.completeHandler = &eis.handleLocalConnectionComplete; - scope(failure) freeIoOp(niop); nev.data.ptr = niop; if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, ns, &nev) == -1) throw new Exception("epoll_ctl " ~ to!string(errno)); - } - } else if(ioop.operation == IoOp.ReadSocketHandle) { - while(true) { - int in_fd; - auto got = read_fd(ioop.fd, ioop.allocatedBuffer.ptr, ioop.allocatedBuffer.length, &in_fd); - if(got == -1) { - if(errno == EAGAIN || errno == EWOULDBLOCK) { - // all done, got it all - if(ioop.completeHandler) - ioop.completeHandler(ioop); - break; + } else { + bool found = false; + foreach(ref pfd; pollfds) { + if(pfd.fd < 0) { + pfd.fd = ns; + found = true; } - throw new Exception("recv " ~ to!string(errno)); } - - if(got == 0) { - if(ioop.closeHandler) - ioop.closeHandler(ioop); - close(ioop.fd); - freeIoOp(ioop); - break; - } - - ioop.bufferLengthUsed = cast(int) got; - ioop.handler(ioop, in_fd); - } - } else if(ioop.operation == IoOp.Read) { - while(true) { - auto got = recv(ioop.fd, ioop.allocatedBuffer.ptr, ioop.allocatedBuffer.length, 0); - if(got == -1) { - if(errno == EAGAIN || errno == EWOULDBLOCK) { - // all done, got it all - if(ioop.completeHandler) - ioop.completeHandler(ioop); - break; - } - throw new Exception("recv " ~ to!string(errno)); - } - - if(got == 0) { - if(ioop.closeHandler) - ioop.closeHandler(ioop); - close(ioop.fd); - freeIoOp(ioop); - break; - } - - ioop.bufferLengthUsed = cast(int) got; - ioop.handler(ioop, -1); + if(!found) + pollfds ~= pollfd(ns, POLLIN); } } - - // EPOLLHUP? } + + bool newConnectionCondition() { + version(linux) + return ioop.fd == sock && (flags & EPOLLIN); + else + return pollfds[idx].fd == sock && (pollfds[idx].revents & POLLIN); + } + + if(newConnectionCondition()) { + newConnection(); + } else if(ioop.operation == IoOp.ReadSocketHandle) { + while(true) { + int in_fd; + auto got = read_fd(ioop.fd, ioop.allocatedBuffer.ptr, ioop.allocatedBuffer.length, &in_fd); + if(got == -1) { + if(errno == EAGAIN || errno == EWOULDBLOCK) { + // all done, got it all + if(ioop.completeHandler) + ioop.completeHandler(ioop); + break; + } + throw new Exception("recv " ~ to!string(errno)); + } + + if(got == 0) { + if(ioop.closeHandler) { + ioop.closeHandler(ioop); + version(linux) {} // nothing needed + else { + foreach(ref pfd; pollfds) { + if(pfd.fd == ioop.fd) + pfd.fd = -1; + } + } + } + close(ioop.fd); + freeIoOp(ioop); + break; + } + + ioop.bufferLengthUsed = cast(int) got; + ioop.handler(ioop, in_fd); + } + } else if(ioop.operation == IoOp.Read) { + while(true) { + auto got = recv(ioop.fd, ioop.allocatedBuffer.ptr, ioop.allocatedBuffer.length, 0); + if(got == -1) { + if(errno == EAGAIN || errno == EWOULDBLOCK) { + // all done, got it all + if(ioop.completeHandler) + ioop.completeHandler(ioop); + break; + } + throw new Exception("recv " ~ to!string(errno)); + } + + if(got == 0) { + if(ioop.closeHandler) + ioop.closeHandler(ioop); + close(ioop.fd); + freeIoOp(ioop); + break; + } + + ioop.bufferLengthUsed = cast(int) got; + ioop.handler(ioop, -1); + } + } + + // EPOLLHUP? } - } else { - // this isn't seriously implemented. - static assert(0); } } else version(Windows) { diff --git a/database.d b/database.d index 94bf103..85f5d97 100644 --- a/database.d +++ b/database.d @@ -376,8 +376,8 @@ string toSql(Database db, Variant a) { // just for convenience; "str".toSql(db); string toSql(string s, Database db) { - if(s is null) - return "NULL"; + //if(s is null) + //return "NULL"; return '\'' ~ db.escape(s) ~ '\''; } diff --git a/database_generation.d b/database_generation.d index 4a2313b..dcf7d33 100644 --- a/database_generation.d +++ b/database_generation.d @@ -112,6 +112,7 @@ string generateCreateTableFor(alias O)() { static foreach(memberName; __traits(allMembers, O)) {{ alias member = __traits(getMember, O, memberName); static if(is(typeof(member) == Constraint!constraintSql, string constraintSql)) { + version(dbgenerate_sqlite) {} else { // FIXME: make it work here too, it is the specifics of the constraint strings if(outputted) { sql ~= ","; } @@ -120,6 +121,7 @@ string generateCreateTableFor(alias O)() { sql ~= " "; sql ~= constraintSql; outputted = true; + } } else static if(is(typeof(member) == Index!Fields, Fields...)) { string fields = ""; static foreach(field; Fields) { @@ -155,27 +157,42 @@ string generateCreateTableFor(alias O)() { else static assert(0, P.stringof); } else static if(is(T == int)) sql ~= " INTEGER NOT NULL"; - else static if(is(T == Serial)) - sql ~= " SERIAL"; // FIXME postgresism - else static if(is(T == string)) + else static if(is(T == Serial)) { + version(dbgenerate_sqlite) + sql ~= " INTEGER PRIMARY KEY AUTOINCREMENT"; + else + sql ~= " SERIAL"; // FIXME postgresism + } else static if(is(T == string)) sql ~= " TEXT NOT NULL"; else static if(is(T == double)) sql ~= " FLOAT NOT NULL"; else static if(is(T == bool)) sql ~= " BOOLEAN NOT NULL"; - else static if(is(T == Timestamp)) - sql ~= " TIMESTAMPTZ NOT NULL"; // FIXME: postgresism - else static if(is(T == enum)) + else static if(is(T == Timestamp)) { + version(dbgenerate_sqlite) + sql ~= " TEXT NOT NULL"; + else + sql ~= " TIMESTAMPTZ NOT NULL"; // FIXME: postgresism + } else static if(is(T == enum)) sql ~= " INTEGER NOT NULL"; // potentially crap but meh static foreach(attr; __traits(getAttributes, member)) { static if(is(typeof(attr) == Default)) { // FIXME: postgresism there, try current_timestamp in sqlite - sql ~= " DEFAULT " ~ attr.sql; + version(dbgenerate_sqlite) { + import std.string; + sql ~= " DEFAULT " ~ std.string.replace(attr.sql, "now()", "current_timestamp"); + } else + sql ~= " DEFAULT " ~ attr.sql; } else static if(is(attr == Unique)) { sql ~= " UNIQUE"; } else static if(is(attr == PrimaryKey)) { - addPostSql("PRIMARY KEY(" ~ memberName ~ ")"); + version(dbgenerate_sqlite) { + static if(is(T == Serial)) {} // skip, it is done above + else + addPostSql("PRIMARY KEY(" ~ memberName ~ ")"); + } else + addPostSql("PRIMARY KEY(" ~ memberName ~ ")"); } else static if(is(attr == ForeignKey!(to, sqlPolicy), alias to, string sqlPolicy)) { string refTable = toTableName(__traits(parent, to).stringof); string refField = to.stringof; @@ -299,9 +316,9 @@ void insert(O)(ref O t, Database db) { } else { // skip and let it auto-fill } - } else static if(is(T == string)) + } else static if(is(T == string)) { builder.addVariable(memberName, __traits(getMember, t, memberName)); - else static if(is(T == double)) + } else static if(is(T == double)) builder.addVariable(memberName, __traits(getMember, t, memberName)); else static if(is(T == bool)) builder.addVariable(memberName, __traits(getMember, t, memberName)); @@ -315,8 +332,14 @@ void insert(O)(ref O t, Database db) { }} import std.conv; - foreach(row; builder.execute(db, "RETURNING id")) // FIXME: postgres-ism - t.id.value = to!int(row[0]); + version(dbgenerate_sqlite) { + builder.execute(db); + foreach(row; db.query("SELECT max(id) FROM " ~ toTableName(O.stringof))) + t.id.value = to!int(row[0]); + } else { + foreach(row; builder.execute(db, "RETURNING id")) // FIXME: postgres-ism + t.id.value = to!int(row[0]); + } } /// @@ -370,7 +393,7 @@ private void populateFromDbVal(V)(ref V val, string value) { } else static if(is(V == Nullable!P, P)) { // FIXME - if(value.length) { + if(value.length && value != "null") { val.isNull = false; val.value = to!P(value); } diff --git a/sqlite.d b/sqlite.d index 8348651..ea45a9d 100644 --- a/sqlite.d +++ b/sqlite.d @@ -121,7 +121,6 @@ class Sqlite : Database { foreach(i, arg; args) { s.bind(cast(int) i + 1, arg); } - return s.execute(); } @@ -263,6 +262,12 @@ struct Statement { Sqlite db; this(Sqlite db, string sql) { + // the arsd convention is zero based ?, but sqlite insists on one based. so this is stupid but still + if(sql.indexOf("?0") != -1) { + foreach_reverse(i; 0 .. 10) + sql = sql.replace("?" ~ to!string(i), "?" ~ to!string(i + 1)); + } + this.db = db; if(sqlite3_prepare_v2(db.db, toStringz(sql), cast(int) sql.length, &s, null) != SQLITE_OK) throw new DatabaseException(db.error()); @@ -487,14 +492,13 @@ template extract(A, T, R...){ void bind (const char[] name, float value){ bind(bindNameLookUp(name), value); } void bind (const char[] name, const byte[] value){ bind(bindNameLookUp(name), value); } + void bind(int col, typeof(null) value){ + if(sqlite3_bind_null(s, col) != SQLITE_OK) + throw new DatabaseException("bind " ~ db.error()); + } void bind(int col, const char[] value){ - if(value is null) { - if(sqlite3_bind_null(s, col) != SQLITE_OK) - throw new DatabaseException("bind " ~ db.error()); - } else { - if(sqlite3_bind_text(s, col, value.ptr, cast(int) value.length, cast(void*)-1) != SQLITE_OK) - throw new DatabaseException("bind " ~ db.error()); - } + if(sqlite3_bind_text(s, col, value.ptr is null ? "" : value.ptr, cast(int) value.length, cast(void*)-1) != SQLITE_OK) + throw new DatabaseException("bind " ~ db.error()); } void bind(int col, float value){ @@ -525,18 +529,27 @@ template extract(A, T, R...){ void bind(int col, Variant v) { if(v.peek!long) bind(col, v.get!long); - if(v.peek!ulong) + else if(v.peek!ulong) bind(col, v.get!ulong); - if(v.peek!int) + else if(v.peek!int) bind(col, v.get!int); - if(v.peek!string) + else if(v.peek!(const(int))) + bind(col, v.get!(const(int))); + else if(v.peek!bool) + bind(col, v.get!bool ? 1 : 0); + else if(v.peek!DateTime) + bind(col, v.get!DateTime.toISOExtString()); + else if(v.peek!string) bind(col, v.get!string); - if(v.peek!float) + else if(v.peek!float) bind(col, v.get!float); - if(v.peek!(byte[])) + else if(v.peek!(byte[])) bind(col, v.get!(byte[])); - if(v.peek!(void*) && v.get!(void*) is null) - bind(col, cast(string) null); + else if(v.peek!(void*) && v.get!(void*) is null) + bind(col, null); + else + bind(col, v.coerce!string); + //assert(0, v.type.toString ~ " " ~ v.coerce!string); } ~this(){