From 904bcd8ce5a25016be65cb59b5f51cb950e5fb3d Mon Sep 17 00:00:00 2001 From: Hanna Dubyna Date: Thu, 11 Sep 2025 17:02:23 +0200 Subject: [PATCH] Completed the assignment --- Week3/3.1-normalization.md | 24 ++ Week3/3.3-sql-injection.md | 33 ++ Week3/homework/mongodb/index.js | 80 ++++- Week3/package-lock.json | 323 ++++++++++++++++++++ Week3/package.json | 17 ++ Week3/scripts/transaction.js | 51 ++++ Week3/scripts/transactions-create-tables.js | 37 +++ Week3/scripts/transactions-insert-values.js | 36 +++ 8 files changed, 587 insertions(+), 14 deletions(-) create mode 100644 Week3/3.1-normalization.md create mode 100644 Week3/3.3-sql-injection.md create mode 100644 Week3/package-lock.json create mode 100644 Week3/package.json create mode 100644 Week3/scripts/transaction.js create mode 100644 Week3/scripts/transactions-create-tables.js create mode 100644 Week3/scripts/transactions-insert-values.js diff --git a/Week3/3.1-normalization.md b/Week3/3.1-normalization.md new file mode 100644 index 000000000..24fd3e3d7 --- /dev/null +++ b/Week3/3.1-normalization.md @@ -0,0 +1,24 @@ +# 3.1 SQL Normalization + +## 1. What columns violate 1NF? +- `food_code` and `food_description` columns contain multiple comma-separated values in a one cell and this is violates 1NF, to be presised - atomicity. +- `dinner_date` column has data in inconsistent formats and it is not strictly a 1NF violation but should be normalized for consistency purpose. + +## 2. What entities do you recognize that could be extracted? +- member (member_id, member_name, member_address) +- dinner (dinner_id, dinner_date, venue_code) +- venue (venue_code, venue_description) +- food (food_code, food_description) +New tables, that can be created to properly model the relationships in normalized database. They are needed for a good design: +- dinner_food (dinner_id, food_code) — to represent which foods were served at each dinner +- member_dinner (member_id, dinner_id) — to represent which members attended which dinners + +## 3. Name all the tables and columns that would make a 3NF compliant solution. +- members: (member_id PK, member_name, member_address) +- venues: (venue_code PK, venue_description) +- dinners: (dinner_id PK, dinner_date, venue_code FK) +- foods: (food_code PK, food_description) +- dinner_foods: (dinner_id FK, food_code FK, PK(dinner_id, food_code)) +- member_dinners: (member_id FK, dinner_id FK, PK(member_id, dinner_id)) + + diff --git a/Week3/3.3-sql-injection.md b/Week3/3.3-sql-injection.md new file mode 100644 index 000000000..c7935b192 --- /dev/null +++ b/Week3/3.3-sql-injection.md @@ -0,0 +1,33 @@ +## 1. Give an example of a malicious name and code value. + +This function is vulnerable because it puts user input directly into the SQL string. + +If somebody pass as `name`: `' OR 1=1 --` +And as `code`: `anything` +The query becomes: + +``` +SELECT Population FROM Country WHERE Name = '' OR 1=1 --' and code = 'anything' +``` + +And the -- makes the rest a comment, so it fetches all records. + + +## 2. Rewrite the function to use parameterized queries. +We need to use parameterized queries so user input is never directly inserted into the SQL string. + +``` +function getPopulation(Country, name, code, cb) { + conn.query( + `SELECT Population FROM ${Country} WHERE Name = $1 and code = $2`, + [name, code], + function (err, result) { + if (err) cb(err); + if (result.length == 0) cb(new Error("Not found")); + cb(null, result[0].Population); + } + ); +} +``` + +This way, the database will treat name and code as values, not as part of the SQL code. \ No newline at end of file diff --git a/Week3/homework/mongodb/index.js b/Week3/homework/mongodb/index.js index 41ee8b618..eac7f15a7 100644 --- a/Week3/homework/mongodb/index.js +++ b/Week3/homework/mongodb/index.js @@ -1,3 +1,5 @@ +require("dotenv").config({ path: "D:/HYF/databases-Cohort53/Week3/.env" }); + const { MongoClient, ServerApiVersion } = require("mongodb"); const { seedDatabase } = require("./seedDatabase.js"); @@ -13,8 +15,29 @@ async function createEpisodeExercise(client) { // Write code that will add this to the collection! + const collection = client.db("databaseWeek3").collection("bob_ross_episodes"); + const newEpisode = { + episode: "S09E13", + title: "MOUNTAIN HIDE-AWAY", + elements: [ + "CIRRUS", + "CLOUDS", + "CONIFER", + "DECIDIOUS", + "GRASS", + "MOUNTAIN", + "MOUNTAINS", + "RIVER", + "SNOWY_MOUNTAIN", + "TREE", + "TREES", + ], + }; + + const result = await collection.insertOne(newEpisode); + console.log( - `Created season 9 episode 13 and the document got the id ${"TODO: fill in variable here"}` + `Created season 9 episode 13 and the document got the id ${result.insertedId}` ); } @@ -26,26 +49,36 @@ async function findEpisodesExercises(client) { // Find the title of episode 2 in season 2 [Should be: WINTER SUN] - console.log( - `The title of episode 2 in season 2 is ${"TODO: fill in variable here"}` - ); + const collection = client.db("databaseWeek3").collection("bob_ross_episodes"); + const episodeTwo = await collection.findOne({ episode: "S02E02" }); + + console.log(`The title of episode 2 in season 2 is ${episodeTwo.title}`); // Find the season and episode number of the episode called "BLACK RIVER" [Should be: S02E06] + const blackRiver = await collection.findOne({ title: "BLACK RIVER" }); + console.log( - `The season and episode number of the "BLACK RIVER" episode is ${"TODO: fill in variable here"}` + `The season and episode number of the "BLACK RIVER" episode is ${blackRiver.episode}` ); // Find all of the episode titles where Bob Ross painted a CLIFF [Should be: NIGHT LIGHT, EVENING SEASCAPE, SURF'S UP, CLIFFSIDE, BY THE SEA, DEEP WILDERNESS HOME, CRIMSON TIDE, GRACEFUL WATERFALL] + const cliffEpisodes = await collection.find({ elements: "CLIFF" }).toArray(); + const cliffTitles = cliffEpisodes.map((episode) => episode.title).join(", "); - console.log( - `The episodes that Bob Ross painted a CLIFF are ${"TODO: fill in variable here"}` - ); + console.log(`The episodes that Bob Ross painted a CLIFF are ${cliffTitles}`); // Find all of the episode titles where Bob Ross painted a CLIFF and a LIGHTHOUSE [Should be: NIGHT LIGHT] + const lighthouseEpisodes = await collection + .find({ elements: { $all: ["CLIFF", "LIGHTHOUSE"] } }) + .toArray(); + const lighthouseTitles = lighthouseEpisodes + .map((episode) => episode.title) + .join(", "); + console.log( - `The episodes that Bob Ross painted a CLIFF and a LIGHTHOUSE are ${"TODO: fill in variable here"}` + `The episodes that Bob Ross painted a CLIFF and a LIGHTHOUSE are ${lighthouseTitles}` ); } @@ -58,17 +91,28 @@ async function updateEpisodeExercises(client) { */ // Episode 13 in season 30 should be called BLUE RIDGE FALLS, yet it is called BLUE RIDGE FALLERS now. Fix that + const collection = client.db("databaseWeek3").collection("bob_ross_episodes"); + + const updateResult = await collection.updateOne( + { episode: "S30E13" }, + + { $set: { title: "BLUE RIDGE FALLS" } } + ); console.log( - `Ran a command to update episode 13 in season 30 and it updated ${"TODO: fill in variable here"} episodes` + `Ran a command to update episode 13 in season 30 and it updated ${updateResult.modifiedCount} episodes` ); // Unfortunately we made a mistake in the arrays and the element type called 'BUSHES' should actually be 'BUSH' as sometimes only one bush was painted. // Update all of the documents in the collection that have `BUSHES` in the elements array to now have `BUSH` // It should update 120 episodes! + const bushResult = await collection.updateMany( + { elements: "BUSHES" }, + { $set: { "elements.$": "BUSH" } } + ); console.log( - `Ran a command to update all the BUSHES to BUSH and it updated ${"TODO: fill in variable here"} episodes` + `Ran a command to update all the BUSHES to BUSH and it updated ${bushResult.modifiedCount} episodes` ); } @@ -77,9 +121,12 @@ async function deleteEpisodeExercise(client) { * It seems an errand episode has gotten into our data. * This is episode 14 in season 31. Please remove it and verify that it has been removed! */ + const collection = client.db("databaseWeek3").collection("bob_ross_episodes"); + + const deleteResult = await collection.deleteOne({ episode: "S31E14" }); console.log( - `Ran a command to delete episode and it deleted ${"TODO: fill in variable here"} episodes` + `Ran a command to delete episode and it deleted ${deleteResult.deletedCount} episodes` ); } @@ -89,15 +136,20 @@ async function main() { `You did not set up the environment variables correctly. Did you create a '.env' file and add a package to create it?` ); } + const client = new MongoClient(process.env.MONGODB_URL, { - useNewUrlParser: true, - useUnifiedTopology: true, serverApi: ServerApiVersion.v1, }); try { await client.connect(); + // creates a collection if it does'nt exist + const collection = client + .db("databaseWeek3") + .collection("bob_ross_episodes"); + await collection.insertOne({ temp: true }); + // Seed our database await seedDatabase(client); diff --git a/Week3/package-lock.json b/Week3/package-lock.json new file mode 100644 index 000000000..513a7c5b4 --- /dev/null +++ b/Week3/package-lock.json @@ -0,0 +1,323 @@ +{ + "name": "week3", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "week3", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "dotenv": "^17.2.2", + "mongodb": "^6.19.0", + "pg": "^8.16.3" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.0.tgz", + "integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/bson": { + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/dotenv": { + "version": "17.2.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.2.tgz", + "integrity": "sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, + "node_modules/mongodb": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.19.0.tgz", + "integrity": "sha512-H3GtYujOJdeKIMLKBT9PwlDhGrQfplABNF1G904w6r5ZXKWyv77aB0X9B+rhmaAwjtllHzaEkvi9mkGVZxs2Bw==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.4", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.3.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/pg": { + "version": "8.16.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", + "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.9.1", + "pg-pool": "^3.10.1", + "pg-protocol": "^1.10.3", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.2.7" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", + "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", + "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", + "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", + "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + } + } +} diff --git a/Week3/package.json b/Week3/package.json new file mode 100644 index 000000000..6f086da1f --- /dev/null +++ b/Week3/package.json @@ -0,0 +1,17 @@ +{ + "name": "week3", + "version": "1.0.0", + "description": "## Agenda", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "dotenv": "^17.2.2", + "mongodb": "^6.19.0", + "pg": "^8.16.3" + } +} diff --git a/Week3/scripts/transaction.js b/Week3/scripts/transaction.js new file mode 100644 index 000000000..74b2136b1 --- /dev/null +++ b/Week3/scripts/transaction.js @@ -0,0 +1,51 @@ +import { Client } from "pg"; + +const client = new Client({ + host: "localhost", + user: "hyfuser", + port: 5432, + password: "hyfpassword", + database: "week3", +}); + +async function runTransfer() { + try { + await client.connect(); + console.log("Connected to PostgreSQL!"); + + await client.query("BEGIN"); + + await client.query(` + UPDATE account + SET balance = balance - 1000 + WHERE account_number = 101 + `); + + await client.query(` + UPDATE account + SET balance = balance + 1000 + WHERE account_number = 102 + `); + + await client.query( + `INSERT INTO account_changes (account_number, amount, changed_date, remark) VALUES + ($1, $2, CURRENT_DATE, $3)`, + [101, -1000, "Transfer to 102"] + ); + + await client.query( + `INSERT INTO account_changes (account_number, amount, changed_date, remark) VALUES + ($1, $2, CURRENT_DATE, $3)`, + [102, 1000, "Transfer from 101"] + ); + + await client.query("COMMIT"); + console.log("Transaction completed successfully."); + } catch (err) { + await client.query("ROLLBACK"); + } finally { + await client.end(); + } +} + +runTransfer(); diff --git a/Week3/scripts/transactions-create-tables.js b/Week3/scripts/transactions-create-tables.js new file mode 100644 index 000000000..af83ab28b --- /dev/null +++ b/Week3/scripts/transactions-create-tables.js @@ -0,0 +1,37 @@ +import { Client } from "pg"; + +const client = new Client({ + host: "localhost", + user: "hyfuser", + port: 5432, + password: "hyfpassword", + database: "week3", +}); + +async function createTables() { + try { + await client.connect(); + console.log("Connected to PostgreSQL!"); + + await client.query(` + CREATE TABLE IF NOT EXISTS account( + account_number INT PRIMARY KEY, + balance INT + ) `); + + await client.query(` + CREATE TABLE IF NOT EXISTS account_changes( + change_number SERIAL PRIMARY KEY, + account_number INT REFERENCES account (account_number), + amount INT, + changed_date DATE, + remark TEXT + ) `); + } catch (err) { + console.log("Error:", err); + } finally { + await client.end(); + } +} + +createTables(); diff --git a/Week3/scripts/transactions-insert-values.js b/Week3/scripts/transactions-insert-values.js new file mode 100644 index 000000000..6bd38efdf --- /dev/null +++ b/Week3/scripts/transactions-insert-values.js @@ -0,0 +1,36 @@ +import { Client } from "pg"; + +const client = new Client({ + host: "localhost", + user: "hyfuser", + port: 5432, + password: "hyfpassword", + database: "week3", +}); + +async function insertValues() { + try { + await client.connect(); + console.log("Connected to PostgreSQL!"); + + await client.query(` + INSERT INTO account (account_number, balance) VALUES + (101, 5000), + (102, 3000), + (103, 7000) + `); + + await client.query(` + INSERT INTO account_changes (account_number, amount, changed_date, remark) VALUES + (101, 1000, '2025-09-10', 'Initial deposit'), + (102, 2000, '2025-09-10', 'Initial deposit'), + (103, 1500, '2025-09-10', 'Initial deposit') + `); + } catch (err) { + console.log(("Error:", err)); + } finally { + await client.end(); + } +} + +insertValues();