Skip to content

Commit 8306bd4

Browse files
authored
Merge pull request #16 from codenotary/sql-support
Add basic SQL support
2 parents 2d7b82d + 7b170bc commit 8306bd4

File tree

7 files changed

+256
-21
lines changed

7 files changed

+256
-21
lines changed

src/client.ts

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import State from './state';
1313
import * as interfaces from './interfaces';
1414
import { verifyInclusion, verifyDualProof } from './verification'
1515
import { CLIENT_INIT_PREFIX, DEFAULT_DATABASE, DEFAULT_ROOTPATH } from './consts'
16-
import Parameters from '../types/parameters'
16+
import { getSQLValue } from './common';
17+
import Parameters, { SQLValue } from '../types/parameters'
1718

1819
class ImmudbClient {
1920
public state: State;
@@ -88,6 +89,7 @@ class ImmudbClient {
8889
return new Promise((resolve) => resolve(ImmudbClient.instance));
8990
} catch (err) {
9091
await ImmudbClient.instance.shutdown();
92+
9193
return new Promise((_, reject) => reject(err));
9294
}
9395
}
@@ -1650,6 +1652,116 @@ class ImmudbClient {
16501652
console.error(err);
16511653
}
16521654
}
1655+
1656+
async SQLExec ({ sql, params = {}, nowait = false }: Parameters.SQLExec): Promise<schemaTypes.SQLExecResult.AsObject | undefined > {
1657+
try {
1658+
const req = new schemaTypes.SQLExecRequest();
1659+
1660+
const sqlParamsList = Object.entries(params).map(([name, value]) => {
1661+
const param = new schemaTypes.NamedParam();
1662+
1663+
param.setName(name)
1664+
param.setValue(getSQLValue(value))
1665+
1666+
return param;
1667+
});
1668+
1669+
req.setParamsList(sqlParamsList);
1670+
req.setSql(sql);
1671+
req.setNowait(nowait);
1672+
1673+
return new Promise((resolve, reject) => this.client.sQLExec(req, this._metadata, (err, res) => {
1674+
if (err) {
1675+
console.error('SQLExec error', err)
1676+
1677+
reject(err)
1678+
} else {
1679+
resolve(res.toObject())
1680+
}
1681+
}))
1682+
} catch(err) {
1683+
console.error(err);
1684+
}
1685+
}
1686+
1687+
async SQLQuery ({ sql, params = {}, reusesnapshot = false }: Parameters.SQLQuery): Promise<Array<Array<SQLValue>> | undefined> {
1688+
try {
1689+
const req = new schemaTypes.SQLQueryRequest();
1690+
1691+
const sqlParamsList = Object.entries(params).map(([name, value]) => {
1692+
const param = new schemaTypes.NamedParam();
1693+
1694+
param.setName(name)
1695+
param.setValue(getSQLValue(value))
1696+
1697+
return param;
1698+
});
1699+
1700+
req.setParamsList(sqlParamsList);
1701+
req.setSql(sql);
1702+
req.setReusesnapshot(reusesnapshot);
1703+
1704+
return new Promise((resolve, reject) => this.client.sQLQuery(req, this._metadata, (err, res) => {
1705+
if (err) {
1706+
console.error('SQLQuery error', err)
1707+
1708+
reject(err)
1709+
} else {
1710+
resolve(
1711+
res
1712+
.getRowsList()
1713+
.map(row => row
1714+
.getValuesList()
1715+
.map(value => value.hasNull()
1716+
? value.getNull()
1717+
: value.hasS()
1718+
? value.getS()
1719+
: value.hasN()
1720+
? value.getN()
1721+
: value.hasB()
1722+
? value.getB()
1723+
: value.hasBs()
1724+
? value.getBs_asU8()
1725+
: null)
1726+
))
1727+
}
1728+
}))
1729+
} catch(err) {
1730+
console.error(err);
1731+
}
1732+
}
1733+
1734+
async SQLListTables (): Promise<Array<Array<SQLValue>> | undefined> {
1735+
try {
1736+
const req = new empty.Empty()
1737+
1738+
return new Promise((resolve, reject) => this.client.listTables(req, this._metadata, (err, res) => {
1739+
if (err) {
1740+
console.error('SQLListTables error', err);
1741+
1742+
reject(err);
1743+
} else {
1744+
resolve(res
1745+
.getRowsList()
1746+
.map(row => row
1747+
.getValuesList()
1748+
.map(value => value.hasNull()
1749+
? value.getNull()
1750+
: value.hasS()
1751+
? value.getS()
1752+
: value.hasN()
1753+
? value.getN()
1754+
: value.hasB()
1755+
? value.getB()
1756+
: value.hasBs()
1757+
? value.getBs_asU8()
1758+
: null)))
1759+
}
1760+
}))
1761+
} catch(err) {
1762+
console.error(err);
1763+
}
1764+
}
16531765
}
16541766

16551767
export default ImmudbClient;

src/common.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import * as schemaTypes from './proto/schema_pb';
2+
import * as google_protobuf_struct_pb from "google-protobuf/google/protobuf/struct_pb";
3+
import { SQLValue } from '../types/parameters';
4+
5+
export const getSQLValue = (value: SQLValue = null) => {
6+
const sqlValue = new schemaTypes.SQLValue();
7+
8+
if (value === null) {
9+
sqlValue.setNull(google_protobuf_struct_pb.NullValue.NULL_VALUE);
10+
}
11+
12+
if (typeof value === 'number') {
13+
sqlValue.setN(value);
14+
}
15+
16+
if (typeof value === 'string') {
17+
sqlValue.setS(value);
18+
}
19+
20+
if (typeof value === 'boolean') {
21+
sqlValue.setB(value);
22+
}
23+
24+
if (ArrayBuffer.isView(value)) {
25+
sqlValue.setBs(value);
26+
}
27+
28+
return sqlValue;
29+
}

tests/tap-parallel-not-ok/client.ts

Lines changed: 90 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const {
1313
IMMUDB_PWD = 'immudb',
1414
} = process.env;
1515

16-
tap.test('database management', async t => {
16+
tap.test('[DATABASE MANAGEMENT]', async t => {
1717
const config: Config = {
1818
host: IMMUDB_HOST,
1919
port: IMMUDB_PORT,
@@ -57,8 +57,8 @@ tap.test('database management', async t => {
5757
const fourthResponse = await immudbClient.set(fourthRequestData);
5858

5959
if (fourthResponse) {
60-
t.equal(fourthResponse.bltxid, 0);
61-
t.equal(fourthResponse.id, 1);
60+
t.equal(fourthResponse.bltxid, 1);
61+
t.equal(fourthResponse.id, 2);
6262
} else {
6363
t.fail('Failed to set');
6464
}
@@ -88,7 +88,7 @@ tap.test('database management', async t => {
8888
}
8989
});
9090

91-
tap.test('user management', async t => {
91+
tap.test('[USER MANAGEMENT]', async t => {
9292
const config: Config = {
9393
host: IMMUDB_HOST,
9494
port: IMMUDB_PORT,
@@ -158,7 +158,7 @@ tap.test('user management', async t => {
158158
}
159159
});
160160

161-
tap.test('operations', async t => {
161+
tap.test('[OPERATIONS]: Regular', async t => {
162162
const config: Config = {
163163
host: IMMUDB_HOST,
164164
port: IMMUDB_PORT,
@@ -234,7 +234,7 @@ tap.test('operations', async t => {
234234
await immudbClient.verifiedSetReference(verifiedSetReferenceRequest)
235235

236236
// test: safely set a reference to an inserted key
237-
const verifiedSetReferenceAtRequest = { key: referenceKey, referencedKey: key, attx: 1 }
237+
const verifiedSetReferenceAtRequest = { key: referenceKey, referencedKey: key, attx: 0 }
238238
await immudbClient.verifiedSetReferenceAt(verifiedSetReferenceAtRequest)
239239

240240
// // test: count keys having the specified value
@@ -311,9 +311,8 @@ tap.test('operations', async t => {
311311
key: `${key}${key}`,
312312
value: `${value}${value}`,
313313
};
314-
let verifiedSetResponse
315314
try {
316-
verifiedSetResponse = await immudbClient.verifiedSet(verifiedSetRequest);
315+
await immudbClient.verifiedSet(verifiedSetRequest);
317316
} catch(err) {
318317
t.fail(err)
319318
}
@@ -328,7 +327,7 @@ tap.test('operations', async t => {
328327
value: `${value}1`,
329328
};
330329
try {
331-
verifiedSetResponse = await immudbClient.verifiedSet(verifiedSetRequest);
330+
await immudbClient.verifiedSet(verifiedSetRequest);
332331
} catch(err) {
333332
t.fail(err)
334333
}
@@ -340,7 +339,7 @@ tap.test('operations', async t => {
340339
value: `${value}2`,
341340
};
342341
try {
343-
verifiedSetResponse = await immudbClient.verifiedSet(verifiedSetRequest);
342+
await immudbClient.verifiedSet(verifiedSetRequest);
344343
} catch(err) {
345344
t.fail(err)
346345
}
@@ -390,7 +389,7 @@ tap.test('operations', async t => {
390389
await immudbClient.zAdd(verifiedZAddRequest)
391390

392391
// test: safely set a secondary index on a key at a specific transaction
393-
const verifiedZAddAtRequest = { set: 'test', score: 32, key, attx: 1 }
392+
const verifiedZAddAtRequest = { set: 'test', score: 32, key, attx: 0 }
394393
await immudbClient.zAddAt(verifiedZAddAtRequest)
395394

396395
t.end();
@@ -399,7 +398,86 @@ tap.test('operations', async t => {
399398
}
400399
});
401400

402-
tap.test('batches', async t => {
401+
tap.test('[OPERATIONS]: SQL', async t => {
402+
const config: Config = {
403+
host: IMMUDB_HOST,
404+
port: IMMUDB_PORT,
405+
autoLogin: false,
406+
};
407+
const immudbClient = await ImmudbClient.getInstance(config);
408+
try {
409+
const rand = 1;
410+
const testDB = 'testdb';
411+
412+
// test: login using the specified username and password
413+
const loginRequest: Parameters.Login = {
414+
user: IMMUDB_USER,
415+
password: IMMUDB_PWD,
416+
};
417+
await immudbClient.login(
418+
loginRequest
419+
);
420+
421+
const listDatabasesResponse = await immudbClient.listDatabases()
422+
if (listDatabasesResponse) {
423+
const { databasesList } = listDatabasesResponse
424+
425+
// let dbExists = false
426+
const dbExists = databasesList.some(({ databasename }) => databasename === testDB)
427+
428+
if (!dbExists) {
429+
// test: create database
430+
const createDatabaseRequest: Parameters.CreateDatabase = { databasename: testDB };
431+
432+
await immudbClient.createDatabase(createDatabaseRequest);
433+
}
434+
}
435+
436+
// test: use database just created
437+
const useDatabaseRequest: Parameters.UseDatabase = { databasename: testDB };
438+
await immudbClient.useDatabase(useDatabaseRequest);
439+
440+
const tableName = `table${ Math.floor(Math.random() * 101) }`
441+
442+
await immudbClient.SQLExec({
443+
sql: `create table ${ tableName } (id integer, name varchar, primary key id);`,
444+
})
445+
446+
await immudbClient.SQLListTables()
447+
448+
const sqlExecParams = [
449+
{
450+
id: 1,
451+
name: 'Joe'
452+
},
453+
{
454+
id: 2,
455+
name: 'Joe'
456+
},
457+
{
458+
id: 3,
459+
name: 'Adam'
460+
},
461+
]
462+
for (const params of sqlExecParams) {
463+
await immudbClient.SQLExec({
464+
sql: `insert into ${ tableName } (id, name) values (@id, @name);`,
465+
params
466+
})
467+
}
468+
469+
await immudbClient.SQLQuery({
470+
sql: `select id,name from ${ tableName } where name=@name;`,
471+
params: { name: 'Joe' }
472+
})
473+
474+
t.end();
475+
} catch (err) {
476+
t.error(err);
477+
}
478+
});
479+
480+
tap.test('[BATCHES]', async t => {
403481
const config: Config = {
404482
host: IMMUDB_HOST,
405483
port: IMMUDB_PORT,

tests/tap-parallel-not-ok/tx.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import tap from 'tap'
22

3-
import { Tx, digestTXe } from '../../src/tx'
3+
import { digestTXe } from '../../src/tx'
44

55
tap.test('tx-related functions', async t => {
66
try {

0 commit comments

Comments
 (0)