Skip to content

Commit 98516cd

Browse files
committed
add @spinframework/spin-postgres3 package
Signed-off-by: karthik2804 <[email protected]>
1 parent 2a1fa67 commit 98516cd

File tree

6 files changed

+681
-0
lines changed

6 files changed

+681
-0
lines changed

package-lock.json

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"name": "@spinframework/spin-postgres3",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "dist/index.js",
6+
"types": "dist/index.d.ts",
7+
"type": "module",
8+
"scripts": {
9+
"build": "tsc && cp -r types/* dist/"
10+
},
11+
"keywords": [],
12+
"author": "",
13+
"license": "Apache-2.0",
14+
"devDependencies": {
15+
"typescript": "^5.7.3"
16+
},
17+
"config": {
18+
"wasiDep": {
19+
"witDeps": [
20+
{
21+
"witPath": "./wit",
22+
"package": "fermyon:[email protected]",
23+
"world": "spin-postgres3"
24+
}
25+
]
26+
}
27+
}
28+
}
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
import * as spinPg from 'spin:postgres/[email protected]';
2+
3+
export type SpinPostgresV3ValueBoolean = { tag: 'boolean'; val: boolean };
4+
export type SpinPostgresV3ValueInt8 = { tag: 'int8'; val: number };
5+
export type SpinPostgresV3ValueInt16 = { tag: 'int16'; val: number };
6+
export type SpinPostgresV3ValueInt32 = { tag: 'int32'; val: number };
7+
export type SpinPostgresV3ValueInt64 = { tag: 'int64'; val: bigint };
8+
export type SpinPostgresV3ValueFloating32 = { tag: 'floating32'; val: number };
9+
export type SpinPostgresV3ValueFloating64 = { tag: 'floating64'; val: number };
10+
export type SpinPostgresV3ValueStr = { tag: 'str'; val: string };
11+
export type SpinPostgresV3ValueBinary = { tag: 'binary'; val: Uint8Array };
12+
export type SpinPostgresV3ValueDate = {
13+
tag: 'date';
14+
val: [number, number, number];
15+
};
16+
export type SpinPostgresV3ValueTime = {
17+
tag: 'time';
18+
val: [number, number, number, number];
19+
};
20+
export type SpinPostgresV3ValueDateTime = {
21+
tag: 'datetime';
22+
val: [number, number, number, number, number, number, number];
23+
};
24+
export type SpinPostgresV3ValueTimeStamp = { tag: 'timestamp'; val: bigint };
25+
export type SpinPostgresV3ValueDbNull = { tag: 'db-null' };
26+
27+
export type SpinPostgresV3ParameterValue =
28+
| SpinPostgresV3ValueBoolean
29+
| SpinPostgresV3ValueInt8
30+
| SpinPostgresV3ValueInt16
31+
| SpinPostgresV3ValueInt32
32+
| SpinPostgresV3ValueInt64
33+
| SpinPostgresV3ValueFloating32
34+
| SpinPostgresV3ValueFloating64
35+
| SpinPostgresV3ValueStr
36+
| SpinPostgresV3ValueBinary
37+
| SpinPostgresV3ValueDate
38+
| SpinPostgresV3ValueTime
39+
| SpinPostgresV3ValueDateTime
40+
| SpinPostgresV3ValueTimeStamp
41+
| SpinPostgresV3ValueDbNull;
42+
43+
export type PostgresV3ParameterValue =
44+
| SpinPostgresV3ParameterValue
45+
| number
46+
| bigint
47+
| boolean
48+
| null
49+
| Uint8Array
50+
| string;
51+
52+
export enum PostgresV3DataType {
53+
PostgresV3Boolean = 'boolean',
54+
PostgresV3Int8 = 'int8',
55+
PostgresV3Int16 = 'int16',
56+
PostgresV3Int32 = 'int32',
57+
PostgresV3Int64 = 'int64',
58+
PostgresV3Floating32 = 'floating32',
59+
PostgresV3Floating64 = 'floating64',
60+
PostgresV3Str = 'str',
61+
PostgresV3Binary = 'binary',
62+
PostgresV3Date = 'date',
63+
PostgresV3Time = 'time',
64+
PostgresV3DateTime = 'datetime',
65+
PostgresV3TimeStamp = 'timestamp',
66+
PostgresV3Other = 'other',
67+
}
68+
69+
export interface PostgresV3Column {
70+
name: string;
71+
dataType: PostgresV3DataType;
72+
}
73+
74+
export interface PostgresV3RowSet {
75+
columns: PostgresV3Column[];
76+
rows: {
77+
[key: string]:
78+
| number
79+
| boolean
80+
| bigint
81+
| null
82+
| string
83+
| Uint8Array
84+
| Date;
85+
}[];
86+
}
87+
88+
export type PostgresV3DbBoolean = { tag: 'boolean'; val: boolean };
89+
export type PostgresV3DbInt8 = { tag: 'int8'; val: number };
90+
export type PostgresV3DbInt16 = { tag: 'int16'; val: number };
91+
export type PostgresV3DbInt32 = { tag: 'int32'; val: number };
92+
export type PostgresV3DbInt64 = { tag: 'int64'; val: number };
93+
export type PostgresV3DbFloating32 = { tag: 'floating32'; val: number };
94+
export type PostgresV3DbFloating64 = { tag: 'floating64'; val: number };
95+
export type PostgresV3DbStr = { tag: 'str'; val: string };
96+
export type PostgresV3DbBinary = { tag: 'binary'; val: Uint8Array };
97+
export type PostgresV3DbDate = { tag: 'date'; val: [number, number, number] };
98+
export type PostgresV3DbTime = {
99+
tag: 'time';
100+
val: [number, number, number, number];
101+
};
102+
export type PostgresV3DbDateTime = {
103+
tag: 'datetime';
104+
val: [number, number, number, number, number, number, number];
105+
};
106+
export type PostgresV3DbTimeastamp = { tag: 'timestamp'; val: bigint };
107+
export type PostgresV3DbNull = { tag: 'db-null' };
108+
export type PostgresV3DbUnsupported = { tag: 'unsupported' };
109+
110+
export type RdbmsDbValue =
111+
| PostgresV3DbBoolean
112+
| PostgresV3DbInt8
113+
| PostgresV3DbInt16
114+
| PostgresV3DbInt32
115+
| PostgresV3DbInt64
116+
| PostgresV3DbFloating32
117+
| PostgresV3DbFloating64
118+
| PostgresV3DbStr
119+
| PostgresV3DbBinary
120+
| PostgresV3DbDate
121+
| PostgresV3DbTime
122+
| PostgresV3DbDateTime
123+
| PostgresV3DbTimeastamp
124+
| PostgresV3DbNull
125+
| PostgresV3DbUnsupported;
126+
127+
export type RdbmsRow = RdbmsDbValue[];
128+
129+
export interface SpinRdbmsRowSet {
130+
columns: PostgresV3Column[];
131+
rows: RdbmsRow[];
132+
}
133+
134+
/**
135+
* Interface representing a PostgreSQL connection with methods for querying and executing statements.
136+
* @interface PostgresConnection
137+
*/
138+
export interface PostgresConnection {
139+
/**
140+
* Queries the database with the specified statement and parameters.
141+
* @param {string} statement - The SQL statement to execute.
142+
* @param {PostgresV3Value[]} params - The parameters for the SQL statement.
143+
* @returns {RdbmsRowSet} The result set of the query.
144+
*/
145+
query: (
146+
statement: string,
147+
params: PostgresV3ParameterValue[],
148+
) => PostgresV3RowSet;
149+
/**
150+
* Executes a statement on the database with the specified parameters.
151+
* @param {string} statement - The SQL statement to execute.
152+
* @param {PostgresV3Value[]} params - The parameters for the SQL statement.
153+
* @returns {number} The number of rows affected by the execution.
154+
*/
155+
execute: (statement: string, params: PostgresV3ParameterValue[]) => bigint;
156+
}
157+
158+
function createPostgresConnection(
159+
connection: spinPg.Connection,
160+
): PostgresConnection {
161+
return {
162+
query: (statement: string, params: PostgresV3ParameterValue[]) => {
163+
let santizedParams = convertRdbmsToWitTypes(params);
164+
let ret = connection.query(statement, santizedParams) as SpinRdbmsRowSet;
165+
let results: PostgresV3RowSet = {
166+
columns: ret.columns,
167+
rows: [],
168+
};
169+
ret.rows.map((k: RdbmsRow, rowIndex: number) => {
170+
results.rows.push({});
171+
k.map((val, valIndex: number) => {
172+
switch (val.tag) {
173+
case 'date': {
174+
// Date (year, month, day)
175+
const [year, month, day] = val.val;
176+
results.rows[rowIndex][results.columns[valIndex].name] = new Date(
177+
Date.UTC(year, month - 1, day),
178+
); // UTC Date object
179+
break;
180+
}
181+
182+
case 'time': {
183+
// Time (hour, minute, second, nanosecond)
184+
const [hour, minute, second, nanosecond] = val.val;
185+
const date = new Date(Date.UTC(1970, 0, 1, hour, minute, second)); // Using an arbitrary date
186+
date.setMilliseconds(nanosecond / 1_000_000); // Convert nanoseconds to milliseconds
187+
results.rows[rowIndex][results.columns[valIndex].name] = date; // UTC Date object with only time set
188+
break;
189+
}
190+
191+
case 'datetime': {
192+
// DateTime (year, month, day, hour, minute, second, nanosecond)
193+
const [year, month, day, hour, minute, second, nanosecond] =
194+
val.val;
195+
const date = new Date(
196+
Date.UTC(year, month - 1, day, hour, minute, second),
197+
);
198+
date.setMilliseconds(nanosecond / 1_000_000); // Convert nanoseconds to milliseconds
199+
results.rows[rowIndex][results.columns[valIndex].name] = date; // Complete UTC Date object
200+
break;
201+
}
202+
203+
case 'timestamp': {
204+
// Timestamp (seconds since epoch)
205+
const seconds = val.val as unknown as number;
206+
results.rows[rowIndex][results.columns[valIndex].name] = new Date(
207+
seconds * 1000,
208+
); // Convert seconds to milliseconds
209+
break;
210+
}
211+
212+
default: {
213+
results.rows[rowIndex][results.columns[valIndex].name] =
214+
val.tag == 'db-null' || val.tag == 'unsupported'
215+
? null
216+
: val.val;
217+
break;
218+
}
219+
}
220+
});
221+
});
222+
return results;
223+
},
224+
execute: (statement: string, params: PostgresV3ParameterValue[]) => {
225+
let santizedParams = convertRdbmsToWitTypes(params);
226+
let ret = connection.execute(statement, santizedParams) as bigint;
227+
return ret;
228+
},
229+
};
230+
}
231+
232+
/**
233+
* Opens a PostgreSQL connection to the specified address.
234+
* @param {string} address - The address of the PostgreSQL server.
235+
* @returns {PostgresConnection} The PostgreSQL connection object.
236+
*/
237+
export function open(address: string): PostgresConnection {
238+
return createPostgresConnection(spinPg.Connection.open(address));
239+
}
240+
241+
function convertRdbmsToWitTypes(
242+
parameters: PostgresV3ParameterValue[],
243+
): SpinPostgresV3ParameterValue[] {
244+
let sanitized: SpinPostgresV3ParameterValue[] = [];
245+
for (let k of parameters) {
246+
if (typeof k === 'object') {
247+
sanitized.push(k as SpinPostgresV3ParameterValue);
248+
continue;
249+
}
250+
if (typeof k === 'string') {
251+
sanitized.push({ tag: 'str', val: k });
252+
continue;
253+
}
254+
if (typeof k === null) {
255+
sanitized.push({ tag: 'db-null' });
256+
continue;
257+
}
258+
if (typeof k === 'boolean') {
259+
sanitized.push({ tag: 'boolean', val: k });
260+
continue;
261+
}
262+
if (typeof k === 'bigint') {
263+
sanitized.push({ tag: 'int64', val: k });
264+
continue;
265+
}
266+
if (typeof k === 'number') {
267+
isFloat(k)
268+
? sanitized.push({ tag: 'floating64', val: k })
269+
: sanitized.push({ tag: 'int32', val: k });
270+
continue;
271+
}
272+
if ((k as any) instanceof Uint8Array) {
273+
sanitized.push({ tag: 'binary', val: k });
274+
continue;
275+
}
276+
}
277+
return sanitized;
278+
}
279+
280+
function isFloat(number: number) {
281+
return number % 1 !== 0;
282+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2020",
4+
"module": "ES2020",
5+
"lib": [
6+
"ES2020",
7+
"WebWorker"
8+
],
9+
"moduleResolution": "node",
10+
"declaration": true,
11+
"outDir": "dist",
12+
"strict": true,
13+
"esModuleInterop": true,
14+
},
15+
"exclude": [
16+
"node_modules",
17+
"dist"
18+
]
19+
}

0 commit comments

Comments
 (0)