easy-postgresql 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +45 -0
  3. package/SECURITY.md +15 -0
  4. package/database/procedure.ts +129 -0
  5. package/database/query.ts +78 -0
  6. package/database/sql_types.d.ts +52 -0
  7. package/database/sql_types.ts +90 -0
  8. package/database/table.ts +378 -0
  9. package/database.ts +26 -0
  10. package/dist/database/procedure.d.ts +26 -0
  11. package/dist/database/procedure.js +107 -0
  12. package/dist/database/procedure.js.map +1 -0
  13. package/dist/database/query.d.ts +13 -0
  14. package/dist/database/query.js +79 -0
  15. package/dist/database/query.js.map +1 -0
  16. package/dist/database/sql_types.d.ts +48 -0
  17. package/dist/database/sql_types.js +50 -0
  18. package/dist/database/sql_types.js.map +1 -0
  19. package/dist/database/table.d.ts +54 -0
  20. package/dist/database/table.js +308 -0
  21. package/dist/database/table.js.map +1 -0
  22. package/dist/database.d.ts +24 -0
  23. package/dist/database.js +28 -0
  24. package/dist/database.js.map +1 -0
  25. package/dist/operations/config.d.ts +22 -0
  26. package/dist/operations/config.js +30 -0
  27. package/dist/operations/config.js.map +1 -0
  28. package/dist/operations/connect_to_server.d.ts +28 -0
  29. package/dist/operations/connect_to_server.js +108 -0
  30. package/dist/operations/connect_to_server.js.map +1 -0
  31. package/dist/operations/data_types.d.ts +6 -0
  32. package/dist/operations/data_types.js +120 -0
  33. package/dist/operations/data_types.js.map +1 -0
  34. package/dist/operations/get_pieces.d.ts +29 -0
  35. package/dist/operations/get_pieces.js +56 -0
  36. package/dist/operations/get_pieces.js.map +1 -0
  37. package/dist/operations/log.d.ts +12 -0
  38. package/dist/operations/log.js +27 -0
  39. package/dist/operations/log.js.map +1 -0
  40. package/dist/operations/procedure_execute.d.ts +18 -0
  41. package/dist/operations/procedure_execute.js +53 -0
  42. package/dist/operations/procedure_execute.js.map +1 -0
  43. package/operations/config.ts +28 -0
  44. package/operations/connect_to_server.ts +111 -0
  45. package/operations/data_types.ts +129 -0
  46. package/operations/get_pieces.ts +63 -0
  47. package/operations/log.ts +27 -0
  48. package/operations/procedure_execute.ts +58 -0
  49. package/package.json +55 -0
  50. package/simple/advanced-test.js +631 -0
  51. package/simple/config.simple.js +9 -0
  52. package/simple/config.simple.ts +9 -0
  53. package/simple/test.js +398 -0
  54. package/simple/test.ts +392 -0
  55. package/tsconfig.json +21 -0
@@ -0,0 +1,631 @@
1
+ const EasyPostgresql = require('../dist/database.js');
2
+ require('colors');
3
+
4
+ const sqlConfig = {
5
+ user: 'postgres',
6
+ password: 'DbmjAzRbdvYQPjwahRudWftWfhWSjJKG',
7
+ host: 'yamanote.proxy.rlwy.net',
8
+ port: 11348,
9
+ database: 'railway'
10
+ };
11
+
12
+ console.log('\neasy-postgresql Advanced Test Suite');
13
+ console.log('=====================================');
14
+ console.log('Starting advanced tests...\n');
15
+
16
+ let passed = 0;
17
+ let failed = 0;
18
+
19
+ function assert(condition, name) {
20
+ if (condition) {
21
+ console.log(` PASS: ${name}`.green);
22
+ passed++;
23
+ } else {
24
+ console.log(` FAIL: ${name}`.red);
25
+ failed++;
26
+ }
27
+ }
28
+
29
+ async function run() {
30
+ EasyPostgresql.Config.logingMode(true);
31
+ await EasyPostgresql.Connect(sqlConfig);
32
+
33
+ // ============================================================
34
+ // SECTION 1: Multiple Table Relationships (JOIN)
35
+ // ============================================================
36
+ console.log('\n=== Section 1: Multi-table JOIN operations ==='.cyan);
37
+
38
+ const users = EasyPostgresql.Table('adv_users');
39
+ const orders = EasyPostgresql.Table('adv_orders');
40
+ const products = EasyPostgresql.Table('adv_products');
41
+ const orderItems = EasyPostgresql.Table('adv_order_items');
42
+
43
+ await users.functions.remove();
44
+ await orders.functions.remove();
45
+ await products.functions.remove();
46
+ await orderItems.functions.remove();
47
+
48
+ await users.functions.create({
49
+ id: EasyPostgresql.Types.int() + EasyPostgresql.Types.options.primaryKey() + EasyPostgresql.Types.options.autoIncrement(),
50
+ name: EasyPostgresql.Types.varchar(100) + EasyPostgresql.Types.options.notNull(),
51
+ email: EasyPostgresql.Types.varchar(100) + EasyPostgresql.Types.options.unique(),
52
+ created_at: EasyPostgresql.Types.datetime() + ' ' + 'DEFAULT NOW()'
53
+ });
54
+
55
+ await products.functions.create({
56
+ id: EasyPostgresql.Types.int() + EasyPostgresql.Types.options.primaryKey() + EasyPostgresql.Types.options.autoIncrement(),
57
+ name: EasyPostgresql.Types.varchar(100) + EasyPostgresql.Types.options.notNull(),
58
+ price: EasyPostgresql.Types.decimal(10, 2) + EasyPostgresql.Types.options.notNull(),
59
+ stock: EasyPostgresql.Types.int() + ' ' + 'DEFAULT 0'
60
+ });
61
+
62
+ await orders.functions.create({
63
+ id: EasyPostgresql.Types.int() + EasyPostgresql.Types.options.primaryKey() + EasyPostgresql.Types.options.autoIncrement(),
64
+ user_id: EasyPostgresql.Types.int() + EasyPostgresql.Types.options.notNull() + EasyPostgresql.Types.options.foreignKey('adv_users', 'id'),
65
+ total_amount: EasyPostgresql.Types.decimal(10, 2) + EasyPostgresql.Types.options.notNull(),
66
+ status: EasyPostgresql.Types.varchar(20) + ' ' + "DEFAULT 'pending'",
67
+ created_at: EasyPostgresql.Types.datetime() + ' ' + 'DEFAULT NOW()'
68
+ });
69
+
70
+ await orderItems.functions.create({
71
+ id: EasyPostgresql.Types.int() + EasyPostgresql.Types.options.primaryKey() + EasyPostgresql.Types.options.autoIncrement(),
72
+ order_id: EasyPostgresql.Types.int() + EasyPostgresql.Types.options.notNull() + EasyPostgresql.Types.options.foreignKey('adv_orders', 'id'),
73
+ product_id: EasyPostgresql.Types.int() + EasyPostgresql.Types.options.notNull() + EasyPostgresql.Types.options.foreignKey('adv_products', 'id'),
74
+ quantity: EasyPostgresql.Types.int() + EasyPostgresql.Types.options.notNull(),
75
+ unit_price: EasyPostgresql.Types.decimal(10, 2) + EasyPostgresql.Types.options.notNull()
76
+ });
77
+
78
+ assert(true, 'All 4 tables created with foreign keys');
79
+
80
+ // Insert data
81
+ const u1 = await users.createOne({ name: 'Alice Johnson', email: 'alice@example.com' });
82
+ const u2 = await users.createOne({ name: 'Bob Smith', email: 'bob@example.com' });
83
+ const u3 = await users.createOne({ name: 'Charlie Brown', email: 'charlie@example.com' });
84
+ assert(u1 && u2 && u3, 'Inserted 3 users');
85
+
86
+ const p1 = await products.createOne({ name: 'Laptop', price: 999.99, stock: 10 });
87
+ const p2 = await products.createOne({ name: 'Mouse', price: 29.99, stock: 50 });
88
+ const p3 = await products.createOne({ name: 'Keyboard', price: 79.99, stock: 30 });
89
+ assert(p1 && p2 && p3, 'Inserted 3 products');
90
+
91
+ // Get actual IDs from auto-increment
92
+ const allUsers = await users.find({}, { limit: 10 });
93
+ const allProducts = await products.find({}, { limit: 10 });
94
+ const alice = allUsers.find(u => u.email === 'alice@example.com');
95
+ const bob = allUsers.find(u => u.email === 'bob@example.com');
96
+ const laptop = allProducts.find(p => p.name === 'Laptop');
97
+ const mouse = allProducts.find(p => p.name === 'Mouse');
98
+
99
+ assert(alice && bob && laptop && mouse, 'Retrieved inserted records by natural keys');
100
+
101
+ // Create orders
102
+ const o1 = await orders.createOne({ user_id: alice.id, total_amount: 1059.97, status: 'completed' });
103
+ const o2 = await orders.createOne({ user_id: bob.id, total_amount: 29.99, status: 'pending' });
104
+
105
+ const allOrders = await orders.find({}, { limit: 10 });
106
+ const order1 = allOrders.find(o => o.total_amount === 1059.97);
107
+ const order2 = allOrders.find(o => o.total_amount === 29.99);
108
+
109
+ // Order items
110
+ const oi1 = await orderItems.createOne({ order_id: order1.id, product_id: laptop.id, quantity: 1, unit_price: 999.99 });
111
+ const oi2 = await orderItems.createOne({ order_id: order1.id, product_id: mouse.id, quantity: 2, unit_price: 29.99 });
112
+ const oi3 = await orderItems.createOne({ order_id: order2.id, product_id: mouse.id, quantity: 1, unit_price: 29.99 });
113
+ assert(oi1 && oi2 && oi3, 'Inserted 3 order items');
114
+
115
+ // JOIN query via raw SQL
116
+ const joinResult = await EasyPostgresql.Query(`
117
+ SELECT u.name AS user_name, u.email, o.id AS order_id, o.status, o.total_amount
118
+ FROM adv_users u
119
+ INNER JOIN adv_orders o ON u.id = o.user_id
120
+ ORDER BY o.id
121
+ `);
122
+ assert(joinResult.status === 200, 'JOIN query returned success');
123
+ assert(joinResult.data.length === 2, 'JOIN returned 2 rows');
124
+ assert(joinResult.data[0].user_name === alice.name, 'JOIN first row correct user');
125
+ assert(joinResult.data[1].user_name === bob.name, 'JOIN second row correct user');
126
+
127
+ // Three-way JOIN
128
+ const threeWayJoin = await EasyPostgresql.Query(`
129
+ SELECT u.name AS user_name, p.name AS product_name, oi.quantity, oi.unit_price
130
+ FROM adv_order_items oi
131
+ INNER JOIN adv_orders o ON oi.order_id = o.id
132
+ INNER JOIN adv_users u ON o.user_id = u.id
133
+ INNER JOIN adv_products p ON oi.product_id = p.id
134
+ ORDER BY u.name, p.name
135
+ `);
136
+ assert(threeWayJoin.status === 200, '3-way JOIN success');
137
+ assert(threeWayJoin.data.length === 3, '3-way JOIN returned 3 rows');
138
+
139
+ // GROUP BY with aggregation
140
+ const aggResult = await EasyPostgresql.Query(`
141
+ SELECT u.name, COUNT(o.id) AS order_count, COALESCE(SUM(o.total_amount), 0) AS total_spent
142
+ FROM adv_users u
143
+ LEFT JOIN adv_orders o ON u.id = o.user_id
144
+ GROUP BY u.id, u.name
145
+ ORDER BY total_spent DESC
146
+ `);
147
+ assert(aggResult.status === 200, 'GROUP BY query success');
148
+ assert(aggResult.data.length === 3, 'GROUP BY returned 3 rows (all users)');
149
+ const aliceAgg = aggResult.data.find(r => r.name === alice.name);
150
+ assert(aliceAgg && aliceAgg.order_count === '1', 'Alice has 1 order');
151
+
152
+ // Clean up section 1
153
+ await orderItems.functions.remove();
154
+ await orders.functions.remove();
155
+ await products.functions.remove();
156
+ await users.functions.remove();
157
+ console.log(' Section 1 cleanup done'.gray);
158
+
159
+ // ============================================================
160
+ // SECTION 2: Data Type Handling
161
+ // ============================================================
162
+ console.log('\n=== Section 2: Data type handling ==='.cyan);
163
+
164
+ const typeTable = EasyPostgresql.Table('adv_types');
165
+ await typeTable.functions.remove();
166
+
167
+ await typeTable.functions.create({
168
+ id: EasyPostgresql.Types.int() + EasyPostgresql.Types.options.primaryKey() + EasyPostgresql.Types.options.autoIncrement(),
169
+ bool_col: EasyPostgresql.Types.bit(),
170
+ int_col: EasyPostgresql.Types.int(),
171
+ bigint_col: EasyPostgresql.Types.bigint(),
172
+ float_col: EasyPostgresql.Types.float(),
173
+ decimal_col: EasyPostgresql.Types.decimal(10, 2),
174
+ varchar_col: EasyPostgresql.Types.varchar(50),
175
+ text_col: EasyPostgresql.Types.text(),
176
+ date_col: EasyPostgresql.Types.date(),
177
+ time_col: EasyPostgresql.Types.time(),
178
+ timestamp_col: EasyPostgresql.Types.datetime(),
179
+ char_col: EasyPostgresql.Types.char(),
180
+ uuid_col: EasyPostgresql.Types.uniqueidentifier(),
181
+ money_col: EasyPostgresql.Types.money(),
182
+ bytea_col: EasyPostgresql.Types.image()
183
+ });
184
+
185
+ const now = new Date();
186
+ const typeInsert = await typeTable.createOne({
187
+ bool_col: true,
188
+ int_col: 42,
189
+ bigint_col: 9007199254740991,
190
+ float_col: 3.14159,
191
+ decimal_col: 99.99,
192
+ varchar_col: 'Hello World',
193
+ text_col: 'Lorem ipsum dolor sit amet',
194
+ date_col: now,
195
+ time_col: '14:30:00',
196
+ timestamp_col: now,
197
+ char_col: 'A',
198
+ uuid_col: '550e8400-e29b-41d4-a716-446655440000',
199
+ money_col: 1234.56,
200
+ bytea_col: Buffer.from('binary data')
201
+ });
202
+ assert(typeInsert, 'Inserted all data types');
203
+
204
+ const typeResult = await typeTable.findOne({ int_col: 42 });
205
+ assert(typeResult !== null, 'Retrieved typed row');
206
+ assert(typeResult.bool_col === true, 'Boolean type preserved');
207
+ assert(typeResult.int_col === 42, 'Integer type preserved');
208
+ assert(typeResult.float_col === 3.14159, 'Float type preserved');
209
+ assert(typeResult.varchar_col === 'Hello World', 'Varchar type preserved');
210
+ assert(typeResult.text_col === 'Lorem ipsum dolor sit amet', 'Text type preserved');
211
+ assert(typeResult.char_col === 'A', 'Char type preserved');
212
+ assert(typeResult.uuid_col === '550e8400-e29b-41d4-a716-446655440000', 'UUID type preserved');
213
+
214
+ await typeTable.functions.remove();
215
+ console.log(' Section 2 cleanup done'.gray);
216
+
217
+ // ============================================================
218
+ // SECTION 3: Column Operations (Full Lifecycle)
219
+ // ============================================================
220
+ console.log('\n=== Section 3: Column operations ==='.cyan);
221
+
222
+ const colTable = EasyPostgresql.Table('adv_columns');
223
+ await colTable.functions.remove();
224
+
225
+ await colTable.functions.create({
226
+ id: EasyPostgresql.Types.int() + EasyPostgresql.Types.options.primaryKey(),
227
+ original_name: EasyPostgresql.Types.varchar(50)
228
+ });
229
+ assert(true, 'Table created with original columns');
230
+
231
+ // Add column
232
+ const addCol = await colTable.functions.updateColumn('new_column').add('VARCHAR(100)');
233
+ assert(addCol, 'Added new column');
234
+
235
+ // Verify the column exists
236
+ const infoAfterAdd = await colTable.functions.info();
237
+ assert(infoAfterAdd && infoAfterAdd.schema.new_column !== undefined, 'New column appears in schema');
238
+
239
+ // Rename column
240
+ const renameCol = await colTable.functions.updateColumn('original_name').rename('renamed_name');
241
+ assert(renameCol, 'Renamed column');
242
+
243
+ const infoAfterRename = await colTable.functions.info();
244
+ assert(infoAfterRename && infoAfterRename.schema.renamed_name !== undefined, 'Renamed column appears in schema');
245
+ assert(infoAfterRename && infoAfterRename.schema.original_name === undefined, 'Original name gone from schema');
246
+
247
+ // Insert data with new schema
248
+ const colInsert = await colTable.createOne({ id: 1, renamed_name: 'test', new_column: 'value' });
249
+ assert(colInsert, 'Inserted with renamed and added columns');
250
+
251
+ const colFind = await colTable.findOne({ id: 1 });
252
+ assert(colFind && colFind.renamed_name === 'test', 'Find with renamed column');
253
+ assert(colFind && colFind.new_column === 'value', 'Find with added column');
254
+
255
+ // Drop added column
256
+ const dropCol = await colTable.functions.updateColumn('new_column').remove();
257
+ assert(dropCol, 'Dropped column');
258
+
259
+ const infoAfterDrop = await colTable.functions.info();
260
+ assert(infoAfterDrop && infoAfterDrop.schema.new_column === undefined, 'Dropped column removed from schema');
261
+
262
+ await colTable.functions.remove();
263
+ console.log(' Section 3 cleanup done'.gray);
264
+
265
+ // ============================================================
266
+ // SECTION 4: Error Handling & Edge Cases
267
+ // ============================================================
268
+ console.log('\n=== Section 4: Error handling & edge cases ==='.cyan);
269
+
270
+ // Invalid SQL
271
+ const badQuery = await EasyPostgresql.Query('SELECTT * FROM nonexistent_table');
272
+ assert(badQuery.status === 500, 'Invalid SQL returns status 500');
273
+ assert(badQuery.data === null, 'Invalid SQL returns null data');
274
+
275
+ // Nonexistent table
276
+ const noTable = await EasyPostgresql.Table('adv_nonexistent').find();
277
+ assert(Array.isArray(noTable) && noTable.length === 0, 'Query on nonexistent table returns empty array');
278
+
279
+ // Empty reference
280
+ const emptyRef = await EasyPostgresql.Table('adv_nonexistent').findOne({});
281
+ assert(emptyRef === null, 'findOne with empty ref on nonexistent returns null');
282
+
283
+ // NULL values
284
+ const nullTestTable = EasyPostgresql.Table('adv_null_test');
285
+ await nullTestTable.functions.remove();
286
+ await nullTestTable.functions.create({
287
+ id: EasyPostgresql.Types.int() + EasyPostgresql.Types.options.primaryKey(),
288
+ nullable_str: EasyPostgresql.Types.varchar(100)
289
+ });
290
+ await nullTestTable.createOne({ id: 1, nullable_str: null });
291
+ const nullFind = await nullTestTable.findOne({ id: 1 });
292
+ assert(nullFind && nullFind.nullable_str === null, 'NULL value handled correctly');
293
+
294
+ await nullTestTable.functions.remove();
295
+
296
+ // Duplicate unique constraint
297
+ const uniqueTable = EasyPostgresql.Table('adv_unique_test');
298
+ await uniqueTable.functions.remove();
299
+ await uniqueTable.functions.create({
300
+ id: EasyPostgresql.Types.int() + EasyPostgresql.Types.options.primaryKey(),
301
+ code: EasyPostgresql.Types.varchar(20) + EasyPostgresql.Types.options.unique()
302
+ });
303
+ await uniqueTable.createOne({ id: 1, code: 'UNIQUE1' });
304
+ const dupResult = await uniqueTable.createOne({ id: 2, code: 'UNIQUE1' });
305
+ assert(dupResult === false, 'Duplicate unique value returns false');
306
+
307
+ await uniqueTable.functions.remove();
308
+ console.log(' Section 4 cleanup done'.gray);
309
+
310
+ // ============================================================
311
+ // SECTION 5: Bulk Operations & Complex Filtering
312
+ // ============================================================
313
+ console.log('\n=== Section 5: Bulk operations & filtering ==='.cyan);
314
+
315
+ const bulkTable = EasyPostgresql.Table('adv_bulk');
316
+ await bulkTable.functions.remove();
317
+ await bulkTable.functions.create({
318
+ id: EasyPostgresql.Types.int() + EasyPostgresql.Types.options.primaryKey(),
319
+ category: EasyPostgresql.Types.varchar(50),
320
+ score: EasyPostgresql.Types.int(),
321
+ active: EasyPostgresql.Types.bit()
322
+ });
323
+
324
+ // Bulk insert
325
+ const bulkData = [
326
+ { id: 1, category: 'A', score: 85, active: true },
327
+ { id: 2, category: 'A', score: 92, active: true },
328
+ { id: 3, category: 'B', score: 78, active: false },
329
+ { id: 4, category: 'B', score: 95, active: true },
330
+ { id: 5, category: 'A', score: 60, active: false },
331
+ { id: 6, category: 'C', score: 88, active: true },
332
+ { id: 7, category: 'C', score: 73, active: true },
333
+ { id: 8, category: 'B', score: 81, active: false },
334
+ { id: 9, category: 'A', score: 99, active: true },
335
+ { id: 10, category: 'C', score: 67, active: false }
336
+ ];
337
+
338
+ let bulkInserts = 0;
339
+ for (const row of bulkData) {
340
+ if (await bulkTable.createOne(row)) bulkInserts++;
341
+ }
342
+ assert(bulkInserts === 10, 'Bulk inserted 10 records');
343
+
344
+ // Complex filter: category = A AND active = true
345
+ const catA_active = await bulkTable.find({ category: 'A', active: true });
346
+ assert(catA_active.length === 3, 'Complex AND filter: category A + active');
347
+
348
+ // LIKE filter
349
+ const likeFilter = await bulkTable.find({}, { likes: { category: 'A?' } });
350
+ assert(likeFilter.length === 4, 'LIKE filter with ? placeholder');
351
+
352
+ // selected_keys
353
+ const selected = await bulkTable.find({ category: 'B' }, { selected_keys: ['id', 'score'] });
354
+ assert(selected.length === 3, 'selected_keys filter correct count');
355
+ assert(selected[0].category === undefined, 'selected_keys excludes non-selected columns');
356
+
357
+ // Limit
358
+ const limited = await bulkTable.find({}, { limit: 3 });
359
+ assert(limited.length === 3, 'LIMIT works correctly');
360
+
361
+ // Ordering via raw query
362
+ const ordered = await EasyPostgresql.Query('SELECT * FROM adv_bulk ORDER BY score DESC');
363
+ assert(ordered.data.length === 10, 'ORDER BY returns all rows');
364
+ assert(ordered.data[0].score === 99, 'ORDER BY DESC first is highest score');
365
+ assert(ordered.data[9].score === 60, 'ORDER BY DESC last is lowest score');
366
+
367
+ // Subquery
368
+ const subquery = await EasyPostgresql.Query(`
369
+ SELECT category, AVG(score)::numeric(10,1) AS avg_score
370
+ FROM adv_bulk
371
+ GROUP BY category
372
+ HAVING AVG(score) > 75
373
+ ORDER BY avg_score DESC
374
+ `);
375
+ assert(subquery.status === 200, 'Subquery with HAVING success');
376
+ assert(subquery.data.length >= 1, 'Subquery returned results');
377
+
378
+ // Multiple deleteAll
379
+ const deleteB_all = await bulkTable.deleteAll({ category: 'B' });
380
+ assert(deleteB_all, 'deleteAll for category B');
381
+ const remainingB = await bulkTable.find({ category: 'B' });
382
+ assert(remainingB.length === 0, 'No B records remaining');
383
+
384
+ const deleteAll = await bulkTable.deleteAll();
385
+ assert(deleteAll, 'deleteAll all remaining');
386
+ const allGone = await bulkTable.find();
387
+ assert(allGone.length === 0, 'All records gone');
388
+
389
+ await bulkTable.functions.remove();
390
+ console.log(' Section 5 cleanup done'.gray);
391
+
392
+ // ============================================================
393
+ // SECTION 6: Update Operations & Conditional Logic
394
+ // ============================================================
395
+ console.log('\n=== Section 6: Update operations ==='.cyan);
396
+
397
+ const updateTable = EasyPostgresql.Table('adv_updates');
398
+ await updateTable.functions.remove();
399
+ await updateTable.functions.create({
400
+ id: EasyPostgresql.Types.int() + EasyPostgresql.Types.options.primaryKey(),
401
+ status: EasyPostgresql.Types.varchar(20) + ' ' + "DEFAULT 'pending'",
402
+ counter: EasyPostgresql.Types.int() + ' ' + 'DEFAULT 0',
403
+ label: EasyPostgresql.Types.varchar(50)
404
+ });
405
+
406
+ await updateTable.createOne({ id: 1, status: 'active', counter: 5, label: 'Test A' });
407
+ await updateTable.createOne({ id: 2, status: 'pending', counter: 0, label: 'Test B' });
408
+ await updateTable.createOne({ id: 3, status: 'active', counter: 10, label: 'Test C' });
409
+
410
+ // Update single field
411
+ const up1 = await updateTable.updateOne({ id: 1 }, { status: 'completed' });
412
+ assert(up1, 'Update single field');
413
+ const afterUp1 = await updateTable.findOne({ id: 1 });
414
+ assert(afterUp1.status === 'completed', 'Updated field has new value');
415
+
416
+ // Update multiple fields
417
+ const up2 = await updateTable.updateOne({ id: 2 }, { status: 'active', counter: 15 });
418
+ assert(up2, 'Update multiple fields');
419
+ const afterUp2 = await updateTable.findOne({ id: 2 });
420
+ assert(afterUp2.status === 'active' && afterUp2.counter === 15, 'Both fields updated');
421
+
422
+ // Update non-existing row
423
+ const up3 = await updateTable.updateOne({ id: 999 }, { status: 'deleted' });
424
+ assert(up3 === false, 'Update non-existing row returns false');
425
+
426
+ // Count after updates
427
+ const activeCount = await updateTable.find({ status: 'active' });
428
+ assert(activeCount.length === 2, 'Active count after updates is 2');
429
+ const completedCount = await updateTable.find({ status: 'completed' });
430
+ assert(completedCount.length === 1, 'Completed count after updates is 1');
431
+
432
+ await updateTable.functions.remove();
433
+ console.log(' Section 6 cleanup done'.gray);
434
+
435
+ // ============================================================
436
+ // SECTION 7: Aggregation Functions
437
+ // ============================================================
438
+ console.log('\n=== Section 7: Aggregation functions ==='.cyan);
439
+
440
+ const aggTable = EasyPostgresql.Table('adv_agg');
441
+ await aggTable.functions.remove();
442
+ await aggTable.functions.create({
443
+ id: EasyPostgresql.Types.int() + EasyPostgresql.Types.options.primaryKey(),
444
+ value: EasyPostgresql.Types.int()
445
+ });
446
+
447
+ for (let i = 0; i < 5; i++) {
448
+ await aggTable.createOne({ id: i + 1, value: (i + 1) * 10 });
449
+ }
450
+
451
+ const aggCount = await aggTable.count();
452
+ assert(aggCount === 5, 'count returns 5');
453
+
454
+ const aggMin = await aggTable.min('value');
455
+ assert(aggMin === 10, 'min returns 10');
456
+
457
+ const aggMax = await aggTable.max('value');
458
+ assert(aggMax === 50, 'max returns 50');
459
+
460
+ const aggAvg = await aggTable.average('value');
461
+ assert(aggAvg === 30, 'average returns 30');
462
+
463
+ const aggLast = await aggTable.last();
464
+ assert(aggLast && aggLast.id === 5, 'last returns row with id 5');
465
+
466
+ const aggFirst = await aggTable.first();
467
+ assert(aggFirst && aggFirst.id === 1, 'first returns row with id 1');
468
+
469
+ // Single - exactly one match
470
+ const aggSingle = await aggTable.single({ id: 3 });
471
+ assert(aggSingle && aggSingle.value === 30, 'single returns exact match');
472
+
473
+ // Single - throws on multiple matches
474
+ try {
475
+ await aggTable.single({});
476
+ assert(false, 'single should throw on multiple matches');
477
+ } catch (e) {
478
+ assert(e.message.includes('more than one'), 'single throws on multiple matches');
479
+ }
480
+
481
+ // SingleOrDefault - no match
482
+ const aggSingleDef = await aggTable.singleOrDefault({ id: 999 });
483
+ assert(aggSingleDef === null, 'singleOrDefault returns null for no match');
484
+
485
+ await aggTable.functions.remove();
486
+ console.log(' Section 7 cleanup done'.gray);
487
+
488
+ // ============================================================
489
+ // SECTION 8: Table Management (getAll, isThere)
490
+ // ============================================================
491
+ console.log('\n=== Section 8: Table management ==='.cyan);
492
+
493
+ const mgmtTable1 = EasyPostgresql.Table('adv_mgmt_a');
494
+ const mgmtTable2 = EasyPostgresql.Table('adv_mgmt_b');
495
+
496
+ await mgmtTable1.functions.remove();
497
+ await mgmtTable2.functions.remove();
498
+
499
+ await mgmtTable1.functions.create({
500
+ id: EasyPostgresql.Types.int() + EasyPostgresql.Types.options.primaryKey()
501
+ });
502
+ await mgmtTable2.functions.create({
503
+ id: EasyPostgresql.Types.int() + EasyPostgresql.Types.options.primaryKey()
504
+ });
505
+
506
+ const isThere1 = await mgmtTable1.functions.isThere();
507
+ const isThere2 = await mgmtTable2.functions.isThere();
508
+ assert(isThere1 && isThere2, 'isThere returns true for existing tables');
509
+
510
+ // getAll
511
+ const allTableInfos = await mgmtTable1.functions.getAll();
512
+ assert(Array.isArray(allTableInfos), 'getAll returns array');
513
+ assert(allTableInfos.some(t => t.name === 'adv_mgmt_a'), 'getAll includes adv_mgmt_a');
514
+ assert(allTableInfos.some(t => t.name === 'adv_mgmt_b'), 'getAll includes adv_mgmt_b');
515
+
516
+ // info
517
+ const info1 = await mgmtTable1.functions.info();
518
+ assert(info1 && info1.name === 'adv_mgmt_a', 'info returns correct name');
519
+ assert(info1.schema && info1.schema.id !== undefined, 'info includes schema');
520
+
521
+ await mgmtTable1.functions.remove();
522
+ await mgmtTable2.functions.remove();
523
+
524
+ const isThereAfter = await mgmtTable1.functions.isThere();
525
+ assert(isThereAfter === false, 'isThere returns false after remove');
526
+
527
+ console.log(' Section 8 cleanup done'.gray);
528
+
529
+ // ============================================================
530
+ // SECTION 9: Concurrent Operations
531
+ // ============================================================
532
+ console.log('\n=== Section 9: Concurrent operations ==='.cyan);
533
+
534
+ const concTable = EasyPostgresql.Table('adv_concurrent');
535
+ await concTable.functions.remove();
536
+ await concTable.functions.create({
537
+ id: EasyPostgresql.Types.int() + EasyPostgresql.Types.options.primaryKey(),
538
+ val: EasyPostgresql.Types.int()
539
+ });
540
+
541
+ const concurrentOps = [];
542
+ for (let i = 0; i < 10; i++) {
543
+ concurrentOps.push(concTable.createOne({ id: i + 1, val: i * 10 }));
544
+ }
545
+ const concResults = await Promise.all(concurrentOps);
546
+ const allSuccess = concResults.every(r => r === true);
547
+ assert(allSuccess, '10 concurrent inserts all succeed');
548
+
549
+ const concCount = await concTable.count();
550
+ assert(concCount === 10, 'Concurrent inserts count matches');
551
+
552
+ await concTable.functions.remove();
553
+ console.log(' Section 9 cleanup done'.gray);
554
+
555
+ // ============================================================
556
+ // SECTION 10: Stored Function with Complex Logic
557
+ // ============================================================
558
+ console.log('\n=== Section 10: Advanced stored function ==='.cyan);
559
+
560
+ await EasyPostgresql.Query(`
561
+ CREATE OR REPLACE FUNCTION adv_calculate_discount(
562
+ p_amount NUMERIC,
563
+ p_customer_type VARCHAR
564
+ )
565
+ RETURNS NUMERIC AS $$
566
+ DECLARE
567
+ discount NUMERIC;
568
+ BEGIN
569
+ IF p_customer_type = 'VIP' THEN
570
+ discount := p_amount * 0.20;
571
+ ELSIF p_customer_type = 'REGULAR' THEN
572
+ discount := p_amount * 0.10;
573
+ ELSIF p_amount > 1000 THEN
574
+ discount := p_amount * 0.05;
575
+ ELSE
576
+ discount := 0;
577
+ END IF;
578
+ RETURN ROUND(discount, 2);
579
+ END;
580
+ $$ LANGUAGE plpgsql;
581
+ `);
582
+
583
+ const funcVIP = await EasyPostgresql.Procedure('adv_calculate_discount').Execute({
584
+ p_amount: 500,
585
+ p_customer_type: 'VIP'
586
+ });
587
+ assert(funcVIP.status === 200, 'VIP discount function executed');
588
+
589
+ const funcRegular = await EasyPostgresql.Procedure('adv_calculate_discount').Execute({
590
+ p_amount: 500,
591
+ p_customer_type: 'REGULAR'
592
+ });
593
+ assert(funcRegular.status === 200, 'Regular discount function executed');
594
+
595
+ const funcHigh = await EasyPostgresql.Procedure('adv_calculate_discount').Execute({
596
+ p_amount: 2000,
597
+ p_customer_type: 'BASIC'
598
+ });
599
+ assert(funcHigh.status === 200, 'High amount discount function executed');
600
+
601
+ const funcNone = await EasyPostgresql.Procedure('adv_calculate_discount').Execute({
602
+ p_amount: 100,
603
+ p_customer_type: 'BASIC'
604
+ });
605
+ assert(funcNone.status === 200, 'No discount function executed');
606
+
607
+ // Function returning TABLE
608
+ await EasyPostgresql.Query('CREATE OR REPLACE FUNCTION adv_top_customers(p_limit INT) RETURNS TABLE(customer_name VARCHAR, total_spent NUMERIC) AS ' + '$$' + ' BEGIN RETURN QUERY SELECT s::VARCHAR, s::NUMERIC FROM generate_series(1, p_limit) AS s; END; ' + '$$' + ' LANGUAGE plpgsql;');
609
+
610
+ const topCust = await EasyPostgresql.Procedure('adv_top_customers').Execute({ p_limit: 5 });
611
+ assert(topCust.status === 200, 'TABLE-returning function executed');
612
+ assert(topCust.data.length === 5, 'TABLE-returning function returned 5 rows');
613
+
614
+ await EasyPostgresql.Query('DROP FUNCTION IF EXISTS adv_calculate_discount');
615
+ await EasyPostgresql.Query('DROP FUNCTION IF EXISTS adv_top_customers');
616
+ console.log(' Section 10 cleanup done'.gray);
617
+
618
+ // ============================================================
619
+ // FINAL RESULTS
620
+ // ============================================================
621
+ console.log('\n' + '='.repeat(50));
622
+ console.log(` Total: ${passed + failed} | Passed: ${passed}`.green + ` | Failed: ${failed}`.red);
623
+ console.log('='.repeat(50) + '\n');
624
+
625
+ process.exit(failed > 0 ? 1 : 0);
626
+ }
627
+
628
+ run().catch(e => {
629
+ console.error('FATAL ERROR:', e);
630
+ process.exit(1);
631
+ });
@@ -0,0 +1,9 @@
1
+ const sqlConfig = {
2
+ user: 'user',
3
+ password: 'password',
4
+ host: '1.2.3.4',
5
+ port: 5432,
6
+ database: 'database'
7
+ };
8
+
9
+ module.exports = { sqlConfig };
@@ -0,0 +1,9 @@
1
+ import { SqlConfig } from '../operations/connect_to_server';
2
+
3
+ export const sqlConfig: SqlConfig = {
4
+ user: 'user',
5
+ password: 'password',
6
+ host: '1.2.3.4',
7
+ port: 5432,
8
+ database: 'database'
9
+ };