Skip to content

Commit fb50238

Browse files
author
Daniele Briggi
committed
feat(statement): server side prepared statement
1 parent 9df82a9 commit fb50238

23 files changed

+500
-311
lines changed

.devcontainer/Dockerfile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FROM oven/bun:debian
2+
3+
# Config Bun
4+
ENV PATH="~/.bun/bin:${PATH}"
5+
RUN ln -s /usr/local/bin/bun /usr/local/bin/node
6+
7+
# Update packages
8+
RUN if [ "debian" == "alpine" ] ; then apk update ; else apt-get update ; fi
9+
10+
# Install Git
11+
RUN if [ "debian" == "alpine" ] ; then apk add git ; else apt-get install -y git ; fi
12+

.devcontainer/devcontainer.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
2+
// README at: https://github.com/marcosgomesneto/bun-devcontainers/tree/main/src/basic-bun
3+
{
4+
"name": "Bun",
5+
"dockerFile": "Dockerfile",
6+
// Configure tool-specific properties.
7+
"customizations": {
8+
// Configure properties specific to VS Code.
9+
"vscode": {
10+
// Add the IDs of extensions you want installed when the container is created.
11+
"extensions": [
12+
"oven.bun-vscode"
13+
]
14+
}
15+
},
16+
"features": {
17+
"ghcr.io/devcontainers/features/node:1": {}
18+
}
19+
}

.vscode/extensions.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
{
2-
"recommendations": ["kavod-io.vscode-jest-test-adapter", "esbenp.prettier-vscode", "github.vscode-github-actions", "hbenl.vscode-test-explorer"]
2+
"recommendations": [
3+
"esbenp.prettier-vscode",
4+
"github.vscode-github-actions",
5+
"hbenl.vscode-test-explorer"
6+
]
37
}

bun.lockb

-1 Bytes
Binary file not shown.

examples/with-javascript-express/package-lock.json

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package-lock.json

Lines changed: 8 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@sqlitecloud/drivers",
3-
"version": "1.0.309",
3+
"version": "1.0.331",
44
"description": "SQLiteCloud drivers for Typescript/Javascript in edge, web and node clients",
55
"main": "./lib/index.js",
66
"types": "./lib/index.d.ts",
@@ -73,7 +73,7 @@
7373
"eslint-plugin-node": "^11.1.0",
7474
"eslint-plugin-prettier": "^3.4.1",
7575
"express": "^4.19.2",
76-
"husky": "^9.0.11",
76+
"husky": "^9.1.7",
7777
"jest": "^29.7.0",
7878
"prettier": "^3.2.5",
7979
"sqlite3": "^5.1.7",

src/drivers/connection-tls.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* connection-tls.ts - connection via tls socket and sqlitecloud protocol
33
*/
44

5-
import { type SQLiteCloudConfig, SQLiteCloudError, type ErrorCallback, type ResultsCallback } from './types'
5+
import { type SQLiteCloudConfig, SQLiteCloudError, type ErrorCallback, type ResultsCallback, SQLiteCloudCommand } from './types'
66
import { SQLiteCloudConnection } from './connection'
77
import { getInitializationCommands } from './utilities'
88
import {
@@ -23,8 +23,6 @@ import { Buffer } from 'buffer'
2323

2424
import * as tls from 'tls'
2525

26-
import fs from 'fs'
27-
2826
/**
2927
* Implementation of SQLiteCloudConnection that connects to the database using specific tls APIs
3028
* that connect to native sockets or tls sockets and communicates via raw, binary protocol.
@@ -104,13 +102,17 @@ export class SQLiteCloudTlsConnection extends SQLiteCloudConnection {
104102
}
105103

106104
/** Will send a command immediately (no queueing), return the rowset/result or throw an error */
107-
transportCommands(commands: string, callback?: ResultsCallback): this {
105+
transportCommands(commands: string | SQLiteCloudCommand, callback?: ResultsCallback): this {
108106
// connection needs to be established?
109107
if (!this.socket) {
110108
callback?.call(this, new SQLiteCloudError('Connection not established', { errorCode: 'ERR_CONNECTION_NOT_ESTABLISHED' }))
111109
return this
112110
}
113111

112+
if (typeof commands === 'string') {
113+
commands = { query: commands } as SQLiteCloudCommand
114+
}
115+
114116
// reset buffer and rowset chunks, define response callback
115117
this.buffer = Buffer.alloc(0)
116118
this.startedOn = new Date()
@@ -148,7 +150,7 @@ export class SQLiteCloudTlsConnection extends SQLiteCloudConnection {
148150
// buffer to accumulate incoming data until an whole command is received and can be parsed
149151
private buffer: Buffer = Buffer.alloc(0)
150152
private startedOn: Date = new Date()
151-
private executingCommands?: string
153+
private executingCommands?: SQLiteCloudCommand
152154

153155
// callback to be called when a command is finished processing
154156
private processCallback?: ResultsCallback

src/drivers/connection-ws.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* transport-ws.ts - handles low level communication with sqlitecloud server via socket.io websocket
33
*/
44

5-
import { SQLiteCloudConfig, SQLiteCloudError, ErrorCallback, ResultsCallback } from './types'
5+
import { SQLiteCloudConfig, SQLiteCloudError, ErrorCallback, ResultsCallback, SQLiteCloudCommand } from './types'
66
import { SQLiteCloudRowset } from './rowset'
77
import { SQLiteCloudConnection } from './connection'
88
import { io, Socket } from 'socket.io-client'
@@ -41,7 +41,7 @@ export class SQLiteCloudWebsocketConnection extends SQLiteCloudConnection {
4141
}
4242

4343
/** Will send a command immediately (no queueing), return the rowset/result or throw an error */
44-
transportCommands(commands: string, callback?: ResultsCallback): this {
44+
transportCommands(commands: string | SQLiteCloudCommand, callback?: ResultsCallback): this {
4545
// connection needs to be established?
4646
if (!this.socket) {
4747
callback?.call(this, new SQLiteCloudError('Connection not established', { errorCode: 'ERR_CONNECTION_NOT_ESTABLISHED' }))

src/drivers/connection.ts

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
* connection.ts - base abstract class for sqlitecloud server connections
33
*/
44

5-
import { SQLiteCloudConfig, SQLiteCloudError, ErrorCallback, ResultsCallback } from './types'
6-
import { validateConfiguration, prepareSql } from './utilities'
5+
import { SQLiteCloudConfig, SQLiteCloudError, ErrorCallback, ResultsCallback, SQLiteCloudCommand } from './types'
6+
import { validateConfiguration } from './utilities'
77
import { OperationsQueue } from './queue'
88
import { anonimizeCommand, getUpdateResults } from './utilities'
99

@@ -62,7 +62,7 @@ export abstract class SQLiteCloudConnection {
6262
protected abstract connectTransport(config: SQLiteCloudConfig, callback?: ErrorCallback): this
6363

6464
/** Send a command, return the rowset/result or throw an error */
65-
protected abstract transportCommands(commands: string, callback?: ResultsCallback): this
65+
protected abstract transportCommands(commands: string | SQLiteCloudCommand, callback?: ResultsCallback): this
6666

6767
/** Will log to console if verbose mode is enabled */
6868
protected log(message: string, ...optionalParams: any[]): void {
@@ -85,7 +85,7 @@ export abstract class SQLiteCloudConnection {
8585
}
8686

8787
/** Will enquee a command to be executed and callback with the resulting rowset/result/error */
88-
public sendCommands(commands: string, callback?: ResultsCallback): this {
88+
public sendCommands(commands: string | SQLiteCloudCommand, callback?: ResultsCallback): this {
8989
this.operations.enqueue(done => {
9090
if (!this.connected) {
9191
const error = new SQLiteCloudError('Connection not established', { errorCode: 'ERR_CONNECTION_NOT_ESTABLISHED' })
@@ -108,32 +108,33 @@ export abstract class SQLiteCloudConnection {
108108
* using backticks and parameters in ${parameter} format. These parameters
109109
* will be properly escaped and quoted like when using a prepared statement.
110110
* @param sql A sql string or a template string in `backticks` format
111+
* A SQLiteCloudCommand when the query is defined with question marks and bindings.
111112
* @returns An array of rows in case of selections or an object with
112113
* metadata in case of insert, update, delete.
113114
*/
114-
public async sql(sql: TemplateStringsArray | string, ...values: any[]): Promise<any> {
115-
let preparedSql = ''
115+
public async sql(sql: TemplateStringsArray | string | SQLiteCloudCommand, ...values: any[]): Promise<any> {
116+
let commands = { query: '' } as SQLiteCloudCommand
116117

117118
// sql is a TemplateStringsArray, the 'raw' property is specific to TemplateStringsArray
118119
if (Array.isArray(sql) && 'raw' in sql) {
120+
let query = ''
119121
sql.forEach((string, i) => {
120-
preparedSql += string + (i < values.length ? '?' : '')
122+
// TemplateStringsArray splits the string before each variable
123+
// used in the template. Add the question mark
124+
// to the end of the string for the number of used variables.
125+
query += string + (i < values.length ? '?' : '')
121126
})
122-
preparedSql = prepareSql(preparedSql, ...values)
127+
commands = { query, parameters: values }
128+
} else if (typeof sql === 'string') {
129+
commands = { query: sql, parameters: values }
130+
} else if (typeof sql === 'object') {
131+
commands = sql as SQLiteCloudCommand
123132
} else {
124-
if (typeof sql === 'string') {
125-
if (values?.length > 0) {
126-
preparedSql = prepareSql(sql, ...values)
127-
} else {
128-
preparedSql = sql
129-
}
130-
} else {
131-
throw new Error('Invalid sql')
132-
}
133+
throw new Error('Invalid sql')
133134
}
134135

135136
return new Promise((resolve, reject) => {
136-
this.sendCommands(preparedSql, (error, results) => {
137+
this.sendCommands(commands, (error, results) => {
137138
if (error) {
138139
reject(error)
139140
} else {

0 commit comments

Comments
 (0)