diff --git a/Week3/homework/Exercise1/SQL-Normalization.md b/Week3/homework/Exercise1/SQL-Normalization.md new file mode 100644 index 000000000..a00a584da --- /dev/null +++ b/Week3/homework/Exercise1/SQL-Normalization.md @@ -0,0 +1,62 @@ +### 3.1. Exercise 1 : SQL Normalization + +The manager of the dinner club would like to manage the information system that assists him to keep track of the dinners +had by members. +Because the manager is not an expert of Information Systems, (s)he uses the following table to store the information. +Please help the manger by using the knowledge of database normal forms. +Save all answers in a text file / MD file. + +1. What columns violate 1NF? + - 1NF - need will be one value, same type, unique names, no dublicated. 1 Box - 1 thing + - dinner_date has many different date formats + - food_code has many values in one cell + - food_description has many values in one cell + +2. What entities do you recognize that could be extracted? + - entities - is things or objects. ERP diagram consist with : entities, atributes, relationships. + - atributes is describe entities. relationships: one to many, many to many, many to one. + - We can see these entities: + Member + Dinner + Venue + Food + Member–Dinner (members join dinners) + Dinner–Food (dinners have many foods) +3. Name all the tables and columns that would make a 3NF compliant solution. +- 3NF - Must be 2NF before. +- No transitive dependency: column cannot depend on a non-key column. + - 1.Member + member_id + member_name + member_address + - 2.Dinner + dinner_id + dinner_date + venue_code + - 3.Venue + venue_code + venue_description + - 4.Food + food_code + food_description + - 5.Dinner_Food + dinner_id + food_code + - 6.Member_Dinner + member_id + dinner_id + +``` ++-----------+---------------+----------------+-----------+-------------+------------+-------------------+-----------+------------------+ +| member_id | member_name | member_address | dinner_id | dinner_date | venue_code | venue_description | food_code | food_description | ++-----------+---------------+----------------+-----------+-------------+------------+-------------------+-----------+------------------+ +| 1 | Amit | 325 Max park | D00001001 | 2020-03-15 | B01 | Grand Ball Room | C1, C2 | Curry, Cake | +| 2 | Ben | 24 Hudson lane | D00001002 | 2020/03/15 | B02 | Zoku Roof Top | S1, C2 | Soup, Cake | +| 3 | Cristina | 516 6th Ave | D00001002 | 2020/03/15 | B02 | Zoku Roof Top | S1, C2 | Soup, Cake | +| 4 | Dan | 89 John St | D00001003 | 20-03-2020 | B03 | Goat Farm | P1, T1, M1| Pie, Tea, Mousse | +| 1 | Amit | 325 Max park | D00001003 | 20-03-2020 | B03 | Goat Farm | P1, T1, M1| Pie, Tea, Mousse | +| 3 | Cristina | 516 6th Ave | D00001004 | Mar 25 '20 | B04 | Mama's Kitchen | F1, M1 | Falafal, Mousse | +| 5 | Gabor | 54 Vivaldi St | D00001005 | Mar 26 '20 | B05 | Hungry Hungary | G1, P2 | Goulash, Pasca | +| 6 | Hema | 9 Peter St | D00001003 | 01-04-2020 | B03 | Goat Farm | P1, T1, M1| Pie, Tea, Mousse | ++-----------+---------------+----------------+-----------+-------------+------------+-------------------+-----------+------------------+ +``` \ No newline at end of file diff --git a/Week3/homework/Exercise2/SQL-Transactions.md b/Week3/homework/Exercise2/SQL-Transactions.md new file mode 100644 index 000000000..2ec0c2d1c --- /dev/null +++ b/Week3/homework/Exercise2/SQL-Transactions.md @@ -0,0 +1,13 @@ +### 3.2. Exercise 2 : SQL Transactions + +1. Create two tables `account` and `account_changes` (write transactions-create-tables.js file) +2. `account` table should have following fields : `account_number, balance`. +3. `account_changes` table should have the following + fields : `change_number, account_number, amount, changed_date, remark`. +4. Choose the appropriate data types and keys for these tables. +5. Insert some sample data in these tables. (write transactions-insert-values.js file) +6. Transfer the amount of 1000 from account number 101 to account number 102 and log the changes in the + table `account_changes`. + Do this in a _single transaction_ (Write transaction.js file) + +Submit all three files (`transactions-create-tables.js`, `transactions-insert-values.js` and `transaction.js`). \ No newline at end of file diff --git a/Week3/homework/Exercise2/account.png b/Week3/homework/Exercise2/account.png new file mode 100644 index 000000000..f507f7dd2 Binary files /dev/null and b/Week3/homework/Exercise2/account.png differ diff --git a/Week3/homework/Exercise2/account_changes.png b/Week3/homework/Exercise2/account_changes.png new file mode 100644 index 000000000..60ab10596 Binary files /dev/null and b/Week3/homework/Exercise2/account_changes.png differ diff --git a/Week3/homework/Exercise2/package-lock.json b/Week3/homework/Exercise2/package-lock.json new file mode 100644 index 000000000..b53e85195 --- /dev/null +++ b/Week3/homework/Exercise2/package-lock.json @@ -0,0 +1,392 @@ +{ + "name": "exercise2", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "exercise2", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "dotenv": "^17.2.3", + "mongodb": "4.1", + "pg": "^8.16.3" + } + }, + "node_modules/@types/node": { + "version": "24.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", + "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "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": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", + "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bson": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.2.tgz", + "integrity": "sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==", + "license": "Apache-2.0", + "dependencies": { + "buffer": "^5.6.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "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", + "optional": true + }, + "node_modules/mongodb": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.1.4.tgz", + "integrity": "sha512-Cv/sk8on/tpvvqbEvR1h03mdyNdyvvO+WhtFlL4jrZ+DSsN/oSQHVqmJQI/sBCqqbOArFcYCAYDfyzqFwV4GSQ==", + "license": "Apache-2.0", + "dependencies": { + "bson": "^4.5.4", + "denque": "^2.0.1", + "mongodb-connection-string-url": "^2.1.0" + }, + "engines": { + "node": ">=12.9.0" + }, + "optionalDependencies": { + "saslprep": "^1.0.3" + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", + "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^11.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/saslprep": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", + "license": "MIT", + "optional": true, + "dependencies": { + "sparse-bitfield": "^3.0.3" + }, + "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", + "optional": true, + "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": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "license": "MIT", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "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": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "license": "MIT", + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "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/homework/Exercise2/package.json b/Week3/homework/Exercise2/package.json new file mode 100644 index 000000000..63765981f --- /dev/null +++ b/Week3/homework/Exercise2/package.json @@ -0,0 +1,18 @@ +{ + "name": "exercise2", + "type": "module", + "version": "1.0.0", + "description": "", + "main": "transaction.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "dotenv": "^17.2.3", + "mongodb": "4.1", + "pg": "^8.16.3" + } +} diff --git a/Week3/homework/Exercise2/transaction.js b/Week3/homework/Exercise2/transaction.js new file mode 100644 index 000000000..a0e658369 --- /dev/null +++ b/Week3/homework/Exercise2/transaction.js @@ -0,0 +1,50 @@ +import { Client } from 'pg'; + +async function main() { + const client = new Client({ + host: "localhost", + user: "hyfuser", + password: "hyfpassword", + database: "transactions", + port: 5432 + }); + + await client.connect(); + + try { + await client.query("BEGIN"); + + await client.query(` + UPDATE account + SET balance = balance - 1000 + WHERE account_number = 101; + `); + + await client.query(` + INSERT INTO account_changes (account_number, amount, changed_date, remark) + VALUES (101, -1000, NOW(), 'Transfer to 102'); + `); + + 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 (102, 1000, NOW(), 'Transfer from 101'); + `); + + await client.query("COMMIT"); + console.log("Transaction completed!"); + } catch (err) { + console.error("Error! Rolling back...", err); + await client.query("ROLLBACK"); + } finally { + await client.end(); + } +} + +main(); diff --git a/Week3/homework/Exercise2/transactions-create-tables.js b/Week3/homework/Exercise2/transactions-create-tables.js new file mode 100644 index 000000000..59c9b9981 --- /dev/null +++ b/Week3/homework/Exercise2/transactions-create-tables.js @@ -0,0 +1,38 @@ +import { Client } from 'pg'; + +async function main() { + const client = new Client({ + host: "localhost", + user: "hyfuser", + password: "hyfpassword", + database: "transactions", + port: 5432 + }); + + await client.connect(); + + await client.query(`DROP TABLE IF EXISTS account_changes;`); + await client.query(`DROP TABLE IF EXISTS account;`); + + await client.query(` + CREATE TABLE account ( + account_number INT PRIMARY KEY, + balance NUMERIC(10,2) + ); + `); + + await client.query(` + CREATE TABLE account_changes ( + change_number SERIAL PRIMARY KEY, + account_number INT, + amount NUMERIC(10,2), + changed_date TIMESTAMP, + remark VARCHAR(255) + ); + `); + + console.log("Tables created!"); + await client.end(); +} + +main(); diff --git a/Week3/homework/Exercise2/transactions-insert-values.js b/Week3/homework/Exercise2/transactions-insert-values.js new file mode 100644 index 000000000..33dc1f598 --- /dev/null +++ b/Week3/homework/Exercise2/transactions-insert-values.js @@ -0,0 +1,33 @@ +import { Client } from 'pg'; + +async function main() { + const client = new Client({ + host: "localhost", + user: "hyfuser", + password: "hyfpassword", + database: "transactions", + port: 5432 + }); + + await client.connect(); + + await client.query(` + INSERT INTO account (account_number, balance) + VALUES + (101, 5000), + (102, 2000) + ON CONFLICT (account_number) DO NOTHING; + `); + + await client.query(` + INSERT INTO account_changes (account_number, amount, changed_date, remark) + VALUES + (101, 0, NOW(), 'Start balance'), + (102, 0, NOW(), 'Start balance'); + `); + + console.log("Sample data inserted!"); + await client.end(); +} + +main(); diff --git a/Week3/homework/Exercise3/injection.md b/Week3/homework/Exercise3/injection.md new file mode 100644 index 000000000..565148d34 --- /dev/null +++ b/Week3/homework/Exercise3/injection.md @@ -0,0 +1,48 @@ +### 3.3. Exercise 3 : SQL injection + +You are given the below function which returns the population of a specific country from the [world](../Week1/world.sql) +database. + +```js +function getPopulation(Country, name, code, 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}'`, + function (err, result) { + if (err) cb(err); + if (result.length == 0) cb(new Error("Not found")); + cb(null, result[0].name); + } + ); +} +``` +1. Give an example of a value that can be passed as `name` and `code` that would take advantage of SQL-injection and ( + fetch all the records in the database) +- Hackers use: 101 OR 1=1 +- 1=1 is always true → so the database shows ALL rows. +- This is bad because it shows secret data. +- SELECT * FROM products +- WHERE name = '' OR 1=1 --'; + +2. Rewrite the function so that it is no longer vulnerable to SQL injection +- If write - 101; DROP database mydb;- This can delete the whole database. Very dangerous. + +3. +```js +function getPopulation(Country, name, code, cb) { + const query = `SELECT Population FROM ${Country} WHERE Name = $1 AND code = $2`; + conn.query(query, [name, code], function (err, result) { + if (err) return cb(err); + if (result.length === 0) return cb(new Error("Not found")); + cb(null, result[0].population); + }); +} + +``` + +- We need use +``` $1 and $2 ``` +instead of 'name' and 'code' +- User input goes into an array, not directly into the SQL text. +- The database treats input as data, not as SQL code. +- General need: Never put raw text from the user into SQL, validate input, clean all user input \ No newline at end of file diff --git a/Week3/homework/mongodb/index.js b/Week3/homework/mongodb/index.js index 41ee8b618..686dddef2 100644 --- a/Week3/homework/mongodb/index.js +++ b/Week3/homework/mongodb/index.js @@ -1,86 +1,75 @@ +require("dotenv").config(); const { MongoClient, ServerApiVersion } = require("mongodb"); const { seedDatabase } = require("./seedDatabase.js"); async function createEpisodeExercise(client) { - /** - * We forgot to add the last episode of season 9. It has this information: - * - * episode: S09E13 - * title: MOUNTAIN HIDE-AWAY - * elements: ["CIRRUS", "CLOUDS", "CONIFER", "DECIDIOUS", "GRASS", "MOUNTAIN", "MOUNTAINS", "RIVER", "SNOWY_MOUNTAIN", "TREE", "TREES"] - */ + const db = client.db("databaseWeek3"); + const collection = db.collection("bob_ross_episodes"); - // Write code that will add this to the collection! + 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}` ); } async function findEpisodesExercises(client) { - /** - * Complete the following exercises. - * The comments indicate what to do and what the result should be! - */ - - // 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"}` - ); - - // Find the season and episode number of the episode called "BLACK RIVER" [Should be: S02E06] + const db = client.db("databaseWeek3"); + const collection = db.collection("bob_ross_episodes"); - console.log( - `The season and episode number of the "BLACK RIVER" episode is ${"TODO: fill in variable here"}` - ); + const ep2S2 = await collection.findOne({ episode: "S02E02" }); + console.log(`The title of episode 2 in season 2 is ${ep2S2.title}`); - // 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 blackRiver = await collection.findOne({ title: "BLACK RIVER" }); + console.log(`The season and episode number of the "BLACK RIVER" episode is ${blackRiver.episode}`); + const cliffEpisodes = await collection.find({ elements: "CLIFF" }).toArray(); 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 ${cliffEpisodes.map(e => e.title).join(", ")}` ); - // Find all of the episode titles where Bob Ross painted a CLIFF and a LIGHTHOUSE [Should be: NIGHT LIGHT] - + const cliffLighthouseEpisodes = await collection.find({ + elements: { $all: ["CLIFF", "LIGHTHOUSE"] } + }).toArray(); 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 ${cliffLighthouseEpisodes.map(e => e.title).join(", ")}` ); } async function updateEpisodeExercises(client) { - /** - * There are some problems in the initial data that was filled in. - * Let's use update functions to update this information. - * - * Note: do NOT change the data.json file - */ - - // Episode 13 in season 30 should be called BLUE RIDGE FALLS, yet it is called BLUE RIDGE FALLERS now. Fix that + const db = client.db("databaseWeek3"); + const collection = db.collection("bob_ross_episodes"); - console.log( - `Ran a command to update episode 13 in season 30 and it updated ${"TODO: fill in variable here"} episodes` + const updateEp = 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 ${updateEp.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! - - console.log( - `Ran a command to update all the BUSHES to BUSH and it updated ${"TODO: fill in variable here"} episodes` + const updateBushes = await collection.updateMany( + { elements: "BUSHES" }, + { $set: { "elements.$": "BUSH" } } ); + console.log(`Ran a command to update all the BUSHES to BUSH and it updated ${updateBushes.modifiedCount} episodes`); } + 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 db = client.db("databaseWeek3"); + const collection = db.collection("bob_ross_episodes"); - console.log( - `Ran a command to delete episode and it deleted ${"TODO: fill in variable here"} episodes` - ); + const deleteResult = await collection.deleteOne({ episode: "S31E14" }); + console.log(`Ran a command to delete episode and it deleted ${deleteResult.deletedCount} episodes`); } async function main() { @@ -90,8 +79,6 @@ async function main() { ); } const client = new MongoClient(process.env.MONGODB_URL, { - useNewUrlParser: true, - useUnifiedTopology: true, serverApi: ServerApiVersion.v1, }); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..872633bb4 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,175 @@ +{ + "name": "databases-cohort54", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "dotenv": "^17.2.3", + "mongodb": "^7.0.0" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.2.tgz", + "integrity": "sha512-QgA5AySqB27cGTXBFmnpifAi7HxoGUeezwo6p9dI03MuDB6Pp33zgclqVb6oVK3j6I9Vesg0+oojW2XxB59SGg==", + "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": "13.0.0", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/bson": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-7.0.0.tgz", + "integrity": "sha512-Kwc6Wh4lQ5OmkqqKhYGKIuELXl+EPYSCObVE6bWsp1T/cGkOCBN0I8wF/T44BiuhHyNi1mmKVPXk60d41xZ7kw==", + "license": "Apache-2.0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "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": "7.0.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.0.0.tgz", + "integrity": "sha512-vG/A5cQrvGGvZm2mTnCSz1LUcbOPl83hfB6bxULKQ8oFZauyox/2xbZOoGNl+64m8VBrETkdGCDBdOsCr3F3jg==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.3.0", + "bson": "^7.0.0", + "mongodb-connection-string-url": "^7.0.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.806.0", + "@mongodb-js/zstd": "^7.0.0", + "gcp-metadata": "^7.0.1", + "kerberos": "^7.0.0", + "mongodb-client-encryption": ">=7.0.0 <7.1.0", + "snappy": "^7.3.2", + "socks": "^2.8.6" + }, + "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": "7.0.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.0.tgz", + "integrity": "sha512-irhhjRVLE20hbkRl4zpAYLnDMM+zIZnp0IDB9akAFFUZp/3XdOfwwddc7y6cNvF2WCEtfTYRwYbIfYa2kVY0og==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^13.0.0", + "whatwg-url": "^14.1.0" + }, + "engines": { + "node": ">=20.19.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/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" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..76fdea618 --- /dev/null +++ b/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "dotenv": "^17.2.3", + "mongodb": "^7.0.0" + } +}