Skip to content

Commit 3015211

Browse files
authored
Merge pull request #80 from SingingBush/feature/systime
Feature/systime
2 parents eac4294 + 0978929 commit 3015211

File tree

14 files changed

+569
-94
lines changed

14 files changed

+569
-94
lines changed

.travis.yml

+22-4
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,25 @@ jobs:
8888

8989
- stage: MS SQL Integration Tests
9090
d: dmd
91-
env: MS_SQL_2017
91+
env: MS_SQL_2017_msodbcsql17
92+
services: docker
93+
# addons:
94+
# apt:
95+
# update: true
96+
# packages: [ libevent-dev, freetds-bin, freetds-common, freetds-dev, tdsodbc ]
97+
before_install:
98+
- chmod 644 .travis/*.sh
99+
- bash .travis/docker-mssql.sh -n "ms-sql" -p "bbk4k77JKH88g54" -v "2017-latest-ubuntu"
100+
- bash .travis/xenial-msodbc.sh -p "msodbcsql17"
101+
before_script:
102+
- "docker exec -it ms-sql /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P bbk4k77JKH88g54 -Q 'CREATE DATABASE ddbctest'"
103+
script:
104+
- dub test --config=ODBC
105+
- cd example && dub build --config=ODBC && ./ddbctest --connection=odbc://localhost --database=ddbctest --user=SA --password=bbk4k77JKH88g54 --driver="ODBC Driver 17 for SQL Server"
106+
107+
# Use MS-SQL via FreeTDS
108+
- d: dmd
109+
env: MS_SQL_2017_FreeTDS
92110
services: docker
93111
addons:
94112
apt:
@@ -97,13 +115,13 @@ jobs:
97115
before_install:
98116
- chmod 644 .travis/*.sh
99117
- bash .travis/docker-mssql.sh -n "ms-sql" -p "bbk4k77JKH88g54" -v "2017-latest-ubuntu"
100-
- bash .travis/xenial-msodbc.sh -p "msodbcsql17"
101118
before_script:
102119
- "docker exec -it ms-sql /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P bbk4k77JKH88g54 -Q 'CREATE DATABASE ddbctest'"
103120
script:
104-
- dub test --config=ODBC
105-
- cd example && dub build --config=ODBC && ./ddbctest --connection=odbc://localhost --database=ddbctest --user=SA --password=bbk4k77JKH88g54 --driver="ODBC Driver 17 for SQL Server"
121+
- cd example && dub build
122+
- ./ddbctest --connection=odbc://localhost --database=ddbctest --user=SA --password=bbk4k77JKH88g54 --driver=FreeTDS
106123

107124
allow_failures:
108125
- d: dmd-beta
109126
- d: gdc
127+
- env: MS_SQL_2017_FreeTDS

docker-compose.yml

+11-11
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
version: '3.7'
22
services:
3-
# mysql:
4-
# # Don't use latest (MySQL Server 8.0) as we cannot currently authenticate to it
5-
# image: mysql:5.7
6-
# restart: always
7-
# ports: ['3306:3306']
8-
# environment:
9-
# # - MYSQL_ROOT_PASSWORD=bbk4k77JKH88g54
10-
# - MYSQL_ALLOW_EMPTY_PASSWORD=yes
11-
# - MYSQL_DATABASE=testdb
12-
# # - MYSQL_USER=travis
13-
# # - MYSQL_PASSWORD=
3+
mysql:
4+
# Don't use latest (MySQL Server 8.0) as we cannot currently authenticate to it
5+
image: mysql:5.7
6+
restart: always
7+
ports: ['3306:3306']
8+
environment:
9+
# - MYSQL_ROOT_PASSWORD=bbk4k77JKH88g54
10+
- MYSQL_ALLOW_EMPTY_PASSWORD=yes
11+
- MYSQL_DATABASE=testdb
12+
- MYSQL_USER=travis
13+
- MYSQL_PASSWORD=bbk4k77JKH88g54
1414
postgres:
1515
image: postgres:latest
1616
restart: always

example/ddbc-test.sqlite

-12 KB
Binary file not shown.

example/source/testddbc.d

+143-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import ddbc.all;
22
import std.stdio;
33
import std.conv;
4+
import std.datetime : Date, DateTime;
5+
import std.datetime.systime : SysTime, Clock;
46
import std.algorithm;
57
import std.getopt;
68
import std.string;
@@ -80,7 +82,7 @@ int main(string[] args)
8082
{
8183
static if(__traits(compiles, (){ import std.experimental.logger; } )) {
8284
import std.experimental.logger;
83-
globalLogLevel(LogLevel.info);
85+
globalLogLevel(LogLevel.all);
8486
}
8587

8688
ConnectionParams par;
@@ -231,32 +233,113 @@ int main(string[] args)
231233
final switch(par.driver)
232234
{
233235
case "sqlite":
236+
stmt.executeUpdate("DROP TABLE IF EXISTS ddbct1");
234237
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS ddbct1 (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name VARCHAR(250), comment MEDIUMTEXT, ts DATETIME)");
235-
stmt.executeUpdate("INSERT INTO ddbct1 (name,comment) VALUES ('name1', 'comment for line 1'), ('name2','comment for line 2 - can be very long')");
238+
stmt.executeUpdate("INSERT INTO ddbct1 (name, comment, ts)
239+
VALUES
240+
('name1', 'comment for line 1', CURRENT_TIMESTAMP),
241+
('name2', 'comment for line 2 - can be very long', CURRENT_TIMESTAMP)");
242+
243+
stmt.executeUpdate("DROP TABLE IF EXISTS employee");
244+
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS employee (
245+
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
246+
name VARCHAR(255) NOT NULL,
247+
flags int null,
248+
dob DATE,
249+
created DATETIME,
250+
updated DATETIME
251+
)");
252+
253+
stmt.executeUpdate(`INSERT INTO employee (name, flags, dob, created, updated)
254+
VALUES
255+
("John", 5, "1976-04-18", "2017-11-23T20:45", "2010-12-30T00:00:00Z"),
256+
("Andrei", 2, "1977-09-11", "2018-02-28T13:45", "2010-12-30T12:10:12Z"),
257+
("Walter", 2, "1986-03-21", "2018-03-08T10:30", "2010-12-30T12:10:04.100Z"),
258+
("Rikki", 3, "1979-05-24", "2018-06-13T11:45", "2010-12-30T12:10:58Z"),
259+
("Iain", 0, "1971-11-12", "2018-11-09T09:33", "20101230T121001Z"),
260+
("Robert", 1, "1966-03-19", CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`);
236261
break;
237262
case "postgresql":
238-
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS ddbct1 (id SERIAL PRIMARY KEY, name VARCHAR(250), comment TEXT, ts TIMESTAMP)");
239-
stmt.executeUpdate("INSERT INTO ddbct1 (name,comment) VALUES ('name1', 'comment for line 1'), ('name2','comment for line 2 - can be very long')");
263+
stmt.executeUpdate("DROP TABLE IF EXISTS ddbct1");
264+
stmt.executeUpdate("CREATE TABLE ddbct1 (id SERIAL PRIMARY KEY, name VARCHAR(250), comment TEXT, ts TIMESTAMP)");
265+
stmt.executeUpdate("INSERT INTO ddbct1 (name, comment, ts) VALUES ('name1', 'comment for line 1', CURRENT_TIMESTAMP), ('name2','comment for line 2 - can be very long', CURRENT_TIMESTAMP)");
266+
267+
stmt.executeUpdate(`DROP TABLE IF EXISTS "employee"`);
268+
stmt.executeUpdate(`CREATE TABLE "employee" (
269+
id SERIAL PRIMARY KEY,
270+
name VARCHAR(255) NOT NULL,
271+
flags int null,
272+
dob DATE,
273+
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
274+
updated TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
275+
)`);
276+
277+
stmt.executeUpdate(`INSERT INTO "employee" ("name", "flags", "dob", "created", "updated")
278+
VALUES
279+
('John', 5, '1976-04-18', TIMESTAMP '2017-11-23 20:45', TIMESTAMPTZ '2010-12-30 00:00:00'),
280+
('Andrei', 2, '1977-09-11', TIMESTAMP '2018-02-28 13:45', TIMESTAMPTZ '2010-12-30 12:10:12'),
281+
('Walter', 2, '1986-03-21', TIMESTAMP '2018-03-08 10:30', TIMESTAMPTZ '2010-12-30 12:10:04.100'),
282+
('Rikki', 3, '1979-05-24', TIMESTAMP '2018-06-13 11:45', TIMESTAMPTZ '2010-12-30 12:10:58'),
283+
('Iain', 0, '1971-11-12', TIMESTAMP '2018-11-09 09:33', TIMESTAMPTZ '2010-12-30 12:10:01'),
284+
('Robert', 1, '1966-03-19', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`);
240285
break;
241286
case "mysql": // MySQL has an underscore in 'AUTO_INCREMENT'
242-
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS ddbct1 (id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(250), comment MEDIUMTEXT, ts DATETIME)");
243-
stmt.executeUpdate("INSERT INTO ddbct1 (name,comment) VALUES ('name1', 'comment for line 1'), ('name2','comment for line 2 - can be very long')");
287+
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS ddbct1 (id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(250), comment MEDIUMTEXT, ts TIMESTAMP)");
288+
stmt.executeUpdate("INSERT INTO ddbct1 (name, comment, ts) VALUES ('name1', 'comment for line 1', CURRENT_TIMESTAMP), ('name2','comment for line 2 - can be very long', CURRENT_TIMESTAMP)");
289+
290+
//stmt.executeUpdate("DROP TABLE IF EXISTS employee");
291+
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS employee (
292+
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
293+
name VARCHAR(255) NOT NULL,
294+
flags int null,
295+
dob DATE,
296+
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
297+
updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
298+
)");
299+
300+
stmt.executeUpdate(`INSERT INTO employee (name, flags, dob, created, updated)
301+
VALUES
302+
('John', 5, '1976-04-18', '2017-11-23T20:45', '2010-12-30T00:00:00'),
303+
('Andrei', 2, '1977-09-11', '2018-02-28T13:45', '2010-12-30T12:10:12'),
304+
('Walter', 2, '1986-03-21', '2018-03-08T10:30', '2010-12-30T12:10:04.100'),
305+
('Rikki', 3, '1979-05-24', '2018-06-13T11:45', '2010-12-30T12:10:58'),
306+
('Iain', 0, '1971-11-12', '2018-11-09T09:33', '2010-12-30T12:10:01'),
307+
('Robert', 1, '1966-03-19', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`);
244308
break;
245309
case "odbc":
246-
stmt.executeUpdate("DROP TABLE IF EXISTS dbo.ddbct1");
310+
stmt.executeUpdate("DROP TABLE IF EXISTS ddbct1");
247311
stmt.executeUpdate("CREATE TABLE ddbct1 (id INT NOT NULL IDENTITY(1,1) PRIMARY KEY, name VARCHAR(250), comment VARCHAR(max), ts DATETIME)");
248-
stmt.executeUpdate("INSERT INTO ddbct1 (name, comment) VALUES ('name1', 'comment for line 1'), ('name2','comment for line 2 - can be very long')");
249-
break;
312+
stmt.executeUpdate("INSERT INTO ddbct1 (name, comment, ts) VALUES ('name1', 'comment for line 1', CURRENT_TIMESTAMP), ('name2','comment for line 2 - can be very long', CURRENT_TIMESTAMP)");
313+
314+
stmt.executeUpdate("DROP TABLE IF EXISTS [employee]");
315+
stmt.executeUpdate("CREATE TABLE [employee] (
316+
[id] INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
317+
[name] VARCHAR(255) NOT NULL,
318+
[flags] int null,
319+
[dob] DATE,
320+
[created] DATETIME default CURRENT_TIMESTAMP,
321+
[updated] DATETIMEOFFSET default CURRENT_TIMESTAMP
322+
)");
323+
324+
stmt.executeUpdate(`INSERT INTO [employee] ([name], [flags], [dob], [created], [updated])
325+
VALUES
326+
('John', 5, '1976-04-18', '2017-11-23 20:45', '2010-12-30 00:00:00'),
327+
('Andrei', 2, '1977-09-11', '2018-02-28 13:45', '2010-12-30 12:10:12'),
328+
('Walter', 2, '1986-03-21', '2018-03-08 10:30', '2010-12-30 12:10:04.100'),
329+
('Rikki', 3, '1979-05-24', '2018-06-13 11:45', '2010-12-30 12:10:58'),
330+
('Iain', 0, '1971-11-12', '2018-11-09 09:33', '2010-12-30 12:10:01'),
331+
('Robert', 1, '1966-03-19', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`);
332+
break;
250333
}
251334
write("Done.\n");
252335

253-
writeln("Testing generic SQL select statements");
336+
writeln(" > Testing generic SQL select statements");
254337
auto rs = stmt.executeQuery("SELECT id, name name_alias, comment, ts FROM ddbct1 ORDER BY id");
255338
while (rs.next())
256339
writeln(to!string(rs.getLong(1)) ~ "\t" ~ rs.getString(2) ~ "\t" ~ rs.getString(3)); // rs.getString(3) was wrapped with strNull - not sure what this did
257340

258341

259-
writeln("Testing prepared SQL statements");
342+
writeln(" > Testing prepared SQL statements");
260343
PreparedStatement ps2 = conn.prepareStatement("SELECT id, name name_alias, comment, ts FROM ddbct1 WHERE id >= ?");
261344
scope(exit) ps2.close();
262345
ps2.setUlong(1, 1);
@@ -265,5 +348,53 @@ int main(string[] args)
265348
writeln(to!string(prs.getLong(1)) ~ "\t" ~ prs.getString(2) ~ "\t" ~ prs.getString(3));
266349
}
267350

351+
writeln("Testing basic POD support");
352+
353+
// our POD object
354+
struct Employee {
355+
long id;
356+
string name;
357+
int flags;
358+
Date dob;
359+
DateTime created;
360+
SysTime updated;
361+
}
362+
363+
immutable SysTime now = Clock.currTime();
364+
365+
writeln(" > select all rows from employee table");
366+
foreach(ref e; conn.createStatement().select!Employee) {
367+
//SysTime nextMonth = now.add!"months"(1);
368+
369+
writeln("\t{id: ", e.id, ", name: ", e.name, ", flags: ", e.flags, ", dob: ", e.dob, ", created: ", e.created, ", updated: ", e.updated, "}");
370+
assert(e.name !is null);
371+
assert(e.dob.year > 1950);
372+
assert(e.created <= cast(DateTime) now);
373+
assert(e.updated <= now);
374+
}
375+
376+
writeln(" > select all rows from employee table WHERE id < 4 ORDER BY name DESC...");
377+
foreach(ref e; conn.createStatement().select!Employee.where("id < 4").orderBy("name desc")) {
378+
writeln("\t{id: ", e.id, ", name: ", e.name, ", flags: ", e.flags, ", dob: ", e.dob, ", created: ", e.created, ", updated: ", e.updated, "}");
379+
assert(e.id < 4);
380+
assert(e.name != "Iain" && e.name != "Robert");
381+
assert(e.flags > 1);
382+
}
383+
384+
// todo: Fix the UPDATE/INSERT functionality for PODs
385+
// Employee e;
386+
// e.name = "Dave Smith";
387+
// e.flags = 35;
388+
// e.dob = Date(1979, 8, 5);
389+
// e.created = cast(DateTime) now;
390+
// e.updated = now;
391+
392+
// if(conn.createStatement().insert!Employee(e)) {
393+
// writeln("Successfully inserted new emplyee: \t{id: ", e.id, ", name: ", e.name, ", flags: ", e.flags, ", dob: ", e.dob, ", created: ", e.created, ", updated: ", e.updated, "}");
394+
// } else {
395+
// write("Failed to INSERT employee");
396+
// assert(false);
397+
// }
398+
268399
return 0;
269-
}
400+
}

examples/odbc_test/source/main.d

+26-23
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ import std.file;
55

66
int main(string[] argv)
77
{
8-
string url = "odbc://server-address/NamedInstance?user=sa,password=test,driver=FreeTDS,database=unittest";
9-
url = cast(string)read("test_connection.txt");
8+
//string url = "odbc://server-address/NamedInstance?user=sa,password=test,driver=FreeTDS,database=unittest";
9+
string url = "ddbc:odbc://localhost,1433?user=sa,password=bbk4k77JKH88g54,driver=FreeTDS";
10+
11+
//url = cast(string)read("test_connection.txt"); // catch FileException
12+
1013
//string url = "postgresql://localhost:5432/ddbctestdb?user=ddbctest,password=ddbctestpass,ssl=true";
1114
//string url = "mysql://localhost:3306/ddbctestdb?user=ddbctest,password=ddbctestpass";
1215
//string url = "sqlite:testdb.sqlite";
@@ -29,40 +32,41 @@ int main(string[] argv)
2932
// execute simple queries to create and fill table
3033

3134
stmt.executeUpdate("IF OBJECT_ID('ddbct1', 'U') IS NOT NULL DROP TABLE ddbct1");
35+
3236
stmt.executeUpdate("CREATE TABLE ddbct1
33-
(id bigint not null primary key,
34-
name varchar(250),
35-
comment varchar(max),
36-
ts datetime)");
37+
(id bigint NOT NULL PRIMARY KEY,
38+
name VARCHAR(250),
39+
comment VARCHAR(max),
40+
ts DATETIME)");
3741
//conn.commit();
3842
stmt.executeUpdate("INSERT INTO ddbct1 (id, name, comment, ts) VALUES
39-
(1, 'aaa', 'comment for line 1', '2016/09/14 15:24:01')");
43+
(1, 'aaa', 'comment for line 1', '2016/09/14 15:24:01')");
4044
stmt.executeUpdate("INSERT INTO ddbct1 (id, name, comment, ts) VALUES
41-
(2, 'bbb', 'comment for line 2 - can be very long', '2016/09/14 15:24:01')");
42-
stmt.executeUpdate(
43-
"INSERT INTO ddbct1 (id, comment, ts) values(3, 'Hello World', '2016/09/14 15:24:01')");
45+
(2, 'bbb', 'comment for line 2 - can be very long', '2016/09/14 15:24:01')");
46+
stmt.executeUpdate("INSERT INTO ddbct1 (id, comment, ts) VALUES
47+
(3, 'Hello World', '2016/09/14 15:24:01')");
4448

4549
// reading DB
46-
auto rs = stmt.executeQuery("SELECT * FROM ddbct1");
50+
//auto rs = stmt.executeQuery("SELECT * FROM ddbct1");
51+
auto rs = stmt.executeQuery("SELECT id, name, comment, ts FROM ddbct1");
52+
4753
while (rs.next())
4854
{
49-
writeln(rs.getVariant(1), "\t", rs.getString(2), "\t", rs.getString(3),
50-
"\t", rs.getVariant(4));
55+
writeln(rs.getVariant(1), "\t", rs.getVariant(2), "\t", rs.getString(3), "\t", rs.getVariant(4));
5156
}
5257

5358
// ODBC bytea blobs test
5459

5560
// fill database with test data
5661
stmt.executeUpdate(`IF OBJECT_ID('user_data', 'U') IS NOT NULL DROP TABLE user_data`);
57-
stmt.executeUpdate(
58-
`CREATE TABLE user_data (id INTEGER PRIMARY KEY, name VARCHAR(255) NOT NULL, flags int)`);
62+
stmt.executeUpdate(`CREATE TABLE user_data (id INTEGER PRIMARY KEY, name VARCHAR(255) NOT NULL, flags int)`);
5963
stmt.executeUpdate(`INSERT INTO user_data (id, name, flags) VALUES
60-
(1, 'John', 5),
61-
(2, 'Andrei', 2),
62-
(3, 'Walter', 2),
63-
(4, 'Rikki', 3),
64-
(5, 'Iain', 0),
65-
(6, 'Robert', 1)`);
64+
(1, 'John', 5),
65+
(2, 'Andrei', 2),
66+
(3, 'Walter', 2),
67+
(4, 'Rikki', 3),
68+
(5, 'Iain', 0),
69+
(6, 'Robert', 1)`);
6670

6771
// our POD object
6872
struct UserData
@@ -84,8 +88,7 @@ int main(string[] argv)
8488
writeln("id:", e.id, " name:", e.name, " flags:", e.flags);
8589
}
8690

87-
writeln(
88-
"reading all user table rows, but fetching only id and name (you will see default value 0 in flags field)");
91+
writeln("reading all user table rows, but fetching only id and name (you will see default value 0 in flags field)");
8992
foreach (ref e; stmt.select!(UserData, "id", "name"))
9093
{
9194
writeln("id:", e.id, " name:", e.name, " flags:", e.flags);

source/ddbc/common.d

+7
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,10 @@ public:
371371
override ulong getFetchSize() {
372372
throw new SQLException("Method not implemented");
373373
}
374+
375+
override std.datetime.systime.SysTime getSysTime(int columnIndex) {
376+
throw new SQLException("Method not implemented");
377+
}
374378
override std.datetime.DateTime getDateTime(int columnIndex) {
375379
throw new SQLException("Method not implemented");
376380
}
@@ -381,6 +385,9 @@ public:
381385
throw new SQLException("Method not implemented");
382386
}
383387

388+
override std.datetime.systime.SysTime getSysTime(string columnName) {
389+
return getSysTime(findColumn(columnName));
390+
}
384391
override std.datetime.DateTime getDateTime(string columnName) {
385392
return getDateTime(findColumn(columnName));
386393
}

0 commit comments

Comments
 (0)