-
Notifications
You must be signed in to change notification settings - Fork 6
Hussam-w3-database #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| function getPopulation(Country, name = "' OR '1'='1", code = "' OR '1'='1", cb) { | ||
| // assuming that connection to the database is established and stored as conn | ||
| conn.query( | ||
| `SELECT Population FROM ${Country} WHERE Name = '${name}' and code = '${code}'`, | ||
| [Country, name, code], | ||
| function (err, result) { | ||
| if (err) cb(err); | ||
| if (result.length == 0) cb(new Error("Not found")); | ||
| cb(null, result[0].name); | ||
| } | ||
| ); | ||
| } | ||
| // the query becomes SELECT Population FROM Country WHERE Name = '' OR '1'='1' AND code = '' OR '1'='1'; | ||
| // which is always true and returns the population of all countries | ||
| // to fix this we can use parameterized queries and whitelist for table names | ||
| function getPopulation(Country, name, code, cb) { | ||
| const allowedTables=['Country']; | ||
| if(!allowedTables.includes(Country)){ | ||
| throw new Error('Invalid table'); | ||
| } | ||
| 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); | ||
| } | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| const {client}=require('./connection.js'); | ||
| async function makeTransaction(){ | ||
| try{ | ||
| await client.query('BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE'); | ||
| const res=await client.query('select balance from account where account_number =$1',[101]); | ||
| console.log('Current balance for account 101:', res.rows[0].balance); | ||
| if(res.rows[0].balance<=1000){ | ||
| throw new Error('Insufficient funds in account 101'); | ||
| } | ||
| await client.query('UPDATE account SET balance=balance - $1 WHERE account_number=$2',[1000,101]); | ||
| await client.query('UPDATE account SET balance=balance + $1 WHERE account_number=$2',[1000,102]); | ||
| await client.query('INSERT INTO account_changes (account_number,amount,remark) VALUES ($1,$2,$3)',[101,-1000,'Transfer to account 102']); | ||
| await client.query('INSERT INTO account_changes(account_number,amount,remark) VALUES ($1,$2,$3)',[102,1000,'Transfer from account 101']); | ||
| await client.query('COMMIT'); | ||
| console.log('Transaction completed successfully'); | ||
| } | ||
| catch(err){ | ||
| await client.query('ROLLBACK'); | ||
| console.error('Error making transaction', err.message); | ||
| } | ||
| finally{ | ||
| await client.end(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| const { Client } = require('pg'); | ||
|
|
||
| const client = new Client({ | ||
| user: 'hyfuser', | ||
| host: 'localhost', | ||
| database: 'FinanceDB', | ||
| password: 'hyfpassword', | ||
| port: 5432, | ||
| }); | ||
|
|
||
| client.connect() | ||
| .then(() => console.log('Connected to PostgreSQL')) | ||
| .catch(err => console.error('Connection error', err.stack)); | ||
|
|
||
| module.exports = client; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| # Database Normalization Answers | ||
|
|
||
| ## 1. Columns that violate 1NF | ||
|
|
||
| The following columns violate 1NF because they contain multiple values in a single cell: | ||
| - `food_code` (e.g., `C1, C2`) | ||
| - `food_description` (e.g., `Curry, Cake`) | ||
|
|
||
| ## 2. Recognized Entities | ||
|
|
||
| Entities that can be extracted: | ||
|
|
||
| 1. **Member** | ||
| - Columns: `member_id`, `member_name`, `member_address` | ||
| 2. **Dinner** | ||
| - Columns: `dinner_id`, `dinner_date`, `venue_code` | ||
| 3. **Venue** | ||
| - Columns: `venue_code`, `venue_description` | ||
| 4. **Food** | ||
| - Columns: `food_code`, `food_description` | ||
|
|
||
| **Relationships:** | ||
| - Many-to-many between Member and Dinner → junction table `Member_Dinner` | ||
| - Many-to-many between Dinner and Food → junction table `Dinner_Food` | ||
| - One-to-Many between dinner and venue | ||
|
|
||
| ## 3. 3NF Compliant Tables and Columns | ||
|
|
||
| ### Member | ||
| - `member_id` (PK) | ||
| - `member_name` | ||
| - `member_address` | ||
|
|
||
| ### Venue | ||
| - `venue_code` (PK) | ||
| - `venue_description` | ||
|
|
||
| ### Dinner | ||
| - `dinner_id` (PK) | ||
| - `dinner_date` | ||
| - `venue_code` (FK → Venue) | ||
|
|
||
| ### Food | ||
| - `food_code` (PK) | ||
| - `food_description` | ||
|
|
||
| ### Member_Dinner (junction table) | ||
| - `member_id` (FK → Member) | ||
| - `dinner_id` (FK → Dinner) | ||
| - PK = (`member_id`, `dinner_id`) | ||
|
|
||
| ### Dinner_Food (junction table) | ||
| - `dinner_id` (FK → Dinner) | ||
| - `food_code` (FK → Food) | ||
| - PK = (`dinner_id`, `food_code`) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| import pkg from "pg"; | ||
| const { Client } = pkg; | ||
| const client1 = new Client({ | ||
| user: "postgres", // change if needed | ||
| host: "localhost", | ||
| database: "testdb", // change if needed | ||
| password: "yourpassword", // change if needed | ||
| port: 5432, | ||
| }); | ||
| const client2 = new Client({ | ||
| user: "postgres", | ||
| host: "localhost", | ||
| database: "testdb", | ||
| password: "yourpassword", | ||
| port: 5432, | ||
| }); | ||
| async function run() { | ||
| await client1.connect(); | ||
| await client2.connect(); | ||
| try { | ||
| // Transaction A (withdraw 100) | ||
| const txA = async () => { | ||
| await client1.query("BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE"); | ||
| const res = await client1.query("SELECT balance FROM accounts WHERE account_id = 1"); | ||
| console.log("TxA sees balance:", res.rows[0].balance); | ||
| await new Promise(r => setTimeout(r, 2000)); // simulate delay | ||
| await client1.query("UPDATE accounts SET balance = balance - 100 WHERE account_id = 1"); | ||
| await client1.query("COMMIT"); | ||
| console.log("TxA committed"); | ||
| }; | ||
| // Transaction B (withdraw 100 at the same time) | ||
| const txB = async () => { | ||
| await client2.query("BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE"); | ||
| const res = await client2.query("SELECT balance FROM accounts WHERE account_id = 1"); | ||
| console.log("TxB sees balance:", res.rows[0].balance); | ||
| await new Promise(r => setTimeout(r, 1000)); // simulate overlap | ||
| await client2.query("UPDATE accounts SET balance = balance - 100 WHERE account_id = 1"); | ||
| await client2.query("COMMIT"); | ||
| console.log("TxB committed"); | ||
| }; | ||
| // Run both "at the same time" | ||
| await Promise.allSettled([txA(), txB()]); | ||
| } catch (err) { | ||
| console.error("Error:", err.message); | ||
| } finally { | ||
| await client1.end(); | ||
| await client2.end(); | ||
| } | ||
| } | ||
| run(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| const{client}=require('./connection.js'); | ||
|
|
||
| async function createTables(){ | ||
| try{ | ||
| await client.query('DROP TABLE IF EXISTS account_changes'); | ||
| await client.query('DROP TABLE IF EXISTS account'); | ||
|
|
||
| const createAccountTableQuery=`CREATE TABLE account( | ||
| account_number INT PRIMARY KEY, | ||
| balance NUMERIC(12,2) NOT NULL);`; | ||
| const creatAccount_changesTableQuery=`CREATE TABLE account_changes( | ||
| change_number SERIAL PRIMARY KEY, | ||
| account_number INT , | ||
| AMOUNT NUMERIC(12,2) NOT NULL, | ||
| changed_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | ||
| remark VARCHAR(255), | ||
| FOREIGN KEY (account_number) REFERENCES account(account_number) ON DELETE CASCADE);`; | ||
| await client.query(createAccountTableQuery); | ||
| await client.query(creatAccount_changesTableQuery); | ||
| console.log('Tables created successfully'); | ||
|
|
||
|
|
||
| } | ||
| catch(err){ | ||
| console.error('Error creating tables', err.message); | ||
| }finally{ | ||
| await client.end(); | ||
| } | ||
| } | ||
| createTables(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| const {client}=require('./connection.js'); | ||
| async function insertValues(){ | ||
| try{ | ||
| const insertAccountQuery=`INSERT INTO account (account_number,balance) VALUES ($1,$2),($3,$4),($5,$6),($7,$8) | ||
| ON CONFLICT (account_number) DO NOTHING;`; | ||
| const values=[ | ||
| [101, 1000], | ||
| [102, 2000], | ||
| [103, 1500], | ||
| [104, 3000] | ||
| ]; | ||
| const res=await client.query(insertAccountQuery, values); | ||
| console.log('Inserted account numbers:', res.rows); | ||
| const insertAccountChangesQuery=`INSERT INTO account_changes (account_number, amount, remark) VALUES | ||
| ($1,$2,$3),($4,$5,$6),($7,$8,$9),($10,$11,$12) | ||
| `; | ||
| const changesValues=[ | ||
| [101, 2000, 'Initial deposit'], | ||
| [102, 500, 'Initial deposit'], | ||
| [103, 1000, 'Initial deposit'], | ||
| [104, 300, 'Initial deposit'] | ||
| ]; | ||
|
|
||
| await client.query(insertAccountChangesQuery, changesValues); | ||
| console.log('Inserted values into account_changes table'); | ||
| } | ||
| catch(err){ | ||
| console.error('Error inserting values', err.message); | ||
| }finally{ | ||
| await client.end(); | ||
| } | ||
| } | ||
| insertValues(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,15 @@ | ||
| require('dotenv').config(); | ||
| const { MongoClient, ServerApiVersion } = require("mongodb"); | ||
|
|
||
|
|
||
| const { seedDatabase } = require("./seedDatabase.js"); | ||
|
|
||
| async function createEpisodeExercise(client) { | ||
| const res=await client.db('DatabaseWeek3').collection('bob_ross_episodes').insertOne({ | ||
| episode: 'S09E13', | ||
| title: 'MOUNTAIN HIDE-AWAY', | ||
| elements: ["CIRRUS", "CLOUDS", "CONIFER", "DECIDIOUS", "GRASS", "MOUNTAIN", "MOUNTAINS", "RIVER", "SNOWY_MOUNTAIN", "TREE", "TREES"] | ||
| }) | ||
| /** | ||
| * We forgot to add the last episode of season 9. It has this information: | ||
| * | ||
|
|
@@ -14,11 +21,12 @@ async function createEpisodeExercise(client) { | |
| // Write code that will add this to the collection! | ||
|
|
||
| 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 ${res.insertedId}` | ||
| ); | ||
| } | ||
|
|
||
| async function findEpisodesExercises(client) { | ||
| const res=await client.db('DatabaseWeek3').collection('bob_ross_episodes').findOne({episode:'S02E02'}) | ||
| /** | ||
| * Complete the following exercises. | ||
| * The comments indicate what to do and what the result should be! | ||
|
|
@@ -27,29 +35,39 @@ 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"}` | ||
| `The title of episode 2 in season 2 is ${res.title}` | ||
| ); | ||
| const res2=await client.db('DatabaseWeek3').collection('bob_ross_episodes').findOne({title:'BLACK RIVER'}) | ||
|
|
||
| // Find the season and episode number of the episode called "BLACK RIVER" [Should be: S02E06] | ||
|
|
||
| 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 ${res2.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 client.db('DatabaseWeek3').collection('bob_ross_episodes').find({elements:{$in:['CLIFF']}}).toArray(); | ||
| const titles=cliffEpisodes.map(episode=>episode.title); | ||
|
|
||
| console.log( | ||
| `The episodes that Bob Ross painted a CLIFF are ${"TODO: fill in variable here"}` | ||
| `The episodes that Bob Ross painted a CLIFF are ${titles.join(", ")}` | ||
| ); | ||
|
|
||
|
|
||
| // Find all of the episode titles where Bob Ross painted a CLIFF and a LIGHTHOUSE [Should be: NIGHT LIGHT] | ||
| const cliffAndLighthouseEpisodes=await client.db('DatabaseWeek3').collection('bob_ross_episodes').find({elements:{$all:['CLIFF','LIGHTHOUSE']}}).toArray(); | ||
| const cliffAndLighthouseTitles=cliffAndLighthouseEpisodes.map(episode=>episode.title); | ||
|
|
||
| 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 ${cliffAndLighthouseTitles.join(", ")}` | ||
| ); | ||
| } | ||
|
|
||
| async function updateEpisodeExercises(client) { | ||
| const updateResult=await client.db('DatabaseWeek3').collection('bob_ross_episodes').updateOne({ | ||
| episode:'S30E13' | ||
| },{$set:{title:'BLUE RIDGE FALLS'}}) | ||
| /** | ||
| * There are some problems in the initial data that was filled in. | ||
| * Let's use update functions to update this information. | ||
|
|
@@ -60,15 +78,23 @@ 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 | ||
|
|
||
| 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` | ||
| ); | ||
| const updateManyResult = await client | ||
| .db('DatabaseWeek3') | ||
| .collection('bob_ross_episodes') | ||
| .updateMany( | ||
| { elements: { $in: ['BUSHES'] } }, | ||
| { $set: { "elements.$[elem]": "BUSH" } }, | ||
| { arrayFilters: [{ "elem": "BUSHES" }] } | ||
| ); | ||
|
|
||
| // 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! | ||
|
|
||
| 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 ${updateManyResult.modifiedCount} episodes` | ||
| ); | ||
| } | ||
|
|
||
|
|
@@ -77,9 +103,10 @@ 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 deleteResult=await client.db('DatabaseWeek3').collection('bob_ross_episodes').deleteOne({episode:'S31E14'}) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: you may notice |
||
|
|
||
| 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` | ||
| ); | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice check of balance beforehand. You can also check whether the accounts exist.