diff --git a/README.md b/README.md
index 2e24d8a3..d3db05b8 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,7 @@ This repository stores a variety of examples demonstrating how to use the Oracle
| [json-relational-duality](./json-relational-duality) | JSON Relational Duality examples
| [machine-learning](./machine-learning) | Oracle Machine Learning examples |
| [optimizer](./optimizer) | Oracle Optimizer and Optimizer Stats examples |
+| [precompilers](./precompilers) | Oracle Pro\*C and Pro\*COBOL precompiler examples |
| [plsql](./plsql) | PL/SQL examples |
| [python](./python) | Python examples |
| [ruby](./ruby) | Ruby examples |
diff --git a/java/autonomous-db-parurl/pom.xml b/java/autonomous-db-parurl/pom.xml
index d414e8d7..4df62e12 100644
--- a/java/autonomous-db-parurl/pom.xml
+++ b/java/autonomous-db-parurl/pom.xml
@@ -31,7 +31,7 @@
org.apache.commons
commons-lang3
- 3.12.0
+ 3.18.0
com.fasterxml.jackson.core
diff --git a/javascript/rest-api/part-1-web-server-basics/hr_app/package-lock.json b/javascript/rest-api/part-1-web-server-basics/hr_app/package-lock.json
index 90c0ff2e..06818e9c 100644
--- a/javascript/rest-api/part-1-web-server-basics/hr_app/package-lock.json
+++ b/javascript/rest-api/part-1-web-server-basics/hr_app/package-lock.json
@@ -9,7 +9,7 @@
"license": "Apache-2.0",
"dependencies": {
"express": "^4.21.2",
- "morgan": "^1.9.1"
+ "morgan": "^1.10.1"
}
},
"node_modules/accepts": {
@@ -532,28 +532,21 @@
}
},
"node_modules/morgan": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",
- "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==",
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz",
+ "integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==",
+ "license": "MIT",
"dependencies": {
"basic-auth": "~2.0.1",
"debug": "2.6.9",
"depd": "~2.0.0",
"on-finished": "~2.3.0",
- "on-headers": "~1.0.2"
+ "on-headers": "~1.1.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
- "node_modules/morgan/node_modules/depd": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
- "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
- "engines": {
- "node": ">= 0.8"
- }
- },
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@@ -590,9 +583,10 @@
}
},
"node_modules/on-headers": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
- "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
+ "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
+ "license": "MIT",
"engines": {
"node": ">= 0.8"
}
diff --git a/javascript/rest-api/part-1-web-server-basics/hr_app/package.json b/javascript/rest-api/part-1-web-server-basics/hr_app/package.json
index a2788ff6..4c2f2a14 100644
--- a/javascript/rest-api/part-1-web-server-basics/hr_app/package.json
+++ b/javascript/rest-api/part-1-web-server-basics/hr_app/package.json
@@ -10,6 +10,6 @@
"license": "Apache-2.0",
"dependencies": {
"express": "^4.21.2",
- "morgan": "^1.9.1"
+ "morgan": "^1.10.1"
}
}
diff --git a/javascript/rest-api/part-2-database-basics/hr_app/package-lock.json b/javascript/rest-api/part-2-database-basics/hr_app/package-lock.json
index ca9567d8..576dc631 100644
--- a/javascript/rest-api/part-2-database-basics/hr_app/package-lock.json
+++ b/javascript/rest-api/part-2-database-basics/hr_app/package-lock.json
@@ -1,36 +1,51 @@
{
"name": "hr_app",
"version": "0.1.1",
- "lockfileVersion": 1,
+ "lockfileVersion": 3,
"requires": true,
- "dependencies": {
- "accepts": {
+ "packages": {
+ "": {
+ "version": "0.1.1",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "express": "^4.21.2",
+ "morgan": "^1.10.1",
+ "oracledb": "^5.1.0"
+ }
+ },
+ "node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
- "requires": {
+ "dependencies": {
"mime-types": "~2.1.34",
"negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
}
},
- "array-flatten": {
+ "node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
},
- "basic-auth": {
+ "node_modules/basic-auth": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
- "requires": {
+ "dependencies": {
"safe-buffer": "5.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8"
}
},
- "body-parser": {
+ "node_modules/body-parser": {
"version": "1.20.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
- "requires": {
+ "dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.5",
"debug": "2.6.9",
@@ -44,141 +59,204 @@
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/body-parser/node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"dependencies": {
- "on-finished": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
- "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
- "requires": {
- "ee-first": "1.1.1"
- }
- }
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
}
},
- "bytes": {
+ "node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
- "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "engines": {
+ "node": ">= 0.8"
+ }
},
- "call-bind-apply-helpers": {
+ "node_modules/call-bind-apply-helpers": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz",
"integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==",
- "requires": {
+ "dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
}
},
- "call-bound": {
+ "node_modules/call-bound": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz",
"integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==",
- "requires": {
+ "dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "content-disposition": {
+ "node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
- "requires": {
+ "dependencies": {
"safe-buffer": "5.2.1"
},
- "dependencies": {
- "safe-buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
- }
+ "engines": {
+ "node": ">= 0.6"
}
},
- "content-type": {
+ "node_modules/content-disposition/node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/content-type": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
- "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "engines": {
+ "node": ">= 0.6"
+ }
},
- "cookie": {
+ "node_modules/cookie": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
- "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w=="
+ "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
+ "engines": {
+ "node": ">= 0.6"
+ }
},
- "cookie-signature": {
+ "node_modules/cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
},
- "debug": {
+ "node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "requires": {
+ "dependencies": {
"ms": "2.0.0"
}
},
- "depd": {
+ "node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
- "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "engines": {
+ "node": ">= 0.8"
+ }
},
- "destroy": {
+ "node_modules/destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
- "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
},
- "dunder-proto": {
+ "node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
- "requires": {
+ "dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
}
},
- "ee-first": {
+ "node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
- "encodeurl": {
+ "node_modules/encodeurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
- "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "engines": {
+ "node": ">= 0.8"
+ }
},
- "es-define-property": {
+ "node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
- "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "engines": {
+ "node": ">= 0.4"
+ }
},
- "es-errors": {
+ "node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
- "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "engines": {
+ "node": ">= 0.4"
+ }
},
- "es-object-atoms": {
+ "node_modules/es-object-atoms": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz",
"integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==",
- "requires": {
+ "dependencies": {
"es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
}
},
- "escape-html": {
+ "node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
},
- "etag": {
+ "node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
- "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
},
- "express": {
+ "node_modules/express": {
"version": "4.21.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
- "requires": {
+ "dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.3",
@@ -211,27 +289,49 @@
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
+ "engines": {
+ "node": ">= 0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/express/node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"dependencies": {
- "on-finished": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
- "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
- "requires": {
- "ee-first": "1.1.1"
- }
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/express/node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
},
- "safe-buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
}
- }
+ ]
},
- "finalhandler": {
+ "node_modules/finalhandler": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
- "requires": {
+ "dependencies": {
"debug": "2.6.9",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
@@ -240,37 +340,50 @@
"statuses": "2.0.1",
"unpipe": "~1.0.0"
},
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/finalhandler/node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"dependencies": {
- "on-finished": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
- "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
- "requires": {
- "ee-first": "1.1.1"
- }
- }
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
}
},
- "forwarded": {
+ "node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
- "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "engines": {
+ "node": ">= 0.6"
+ }
},
- "fresh": {
+ "node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
- "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "engines": {
+ "node": ">= 0.6"
+ }
},
- "function-bind": {
+ "node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
- "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
- "get-intrinsic": {
+ "node_modules/get-intrinsic": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz",
"integrity": "sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==",
- "requires": {
+ "dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"dunder-proto": "^1.0.0",
"es-define-property": "^1.0.1",
@@ -281,204 +394,294 @@
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "gopd": {
+ "node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
- "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
- "has-symbols": {
+ "node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
- "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
- "hasown": {
+ "node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
- "requires": {
+ "dependencies": {
"function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
}
},
- "http-errors": {
+ "node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
- "requires": {
+ "dependencies": {
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
}
},
- "iconv-lite": {
+ "node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
- "requires": {
+ "dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
}
},
- "inherits": {
+ "node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
- "ipaddr.js": {
+ "node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
- "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "engines": {
+ "node": ">= 0.10"
+ }
},
- "math-intrinsics": {
+ "node_modules/math-intrinsics": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.0.0.tgz",
- "integrity": "sha512-4MqMiKP90ybymYvsut0CH2g4XWbfLtmlCkXmtmdcDCxNB+mQcu1w/1+L/VD7vi/PSv7X2JYV7SCcR+jiPXnQtA=="
+ "integrity": "sha512-4MqMiKP90ybymYvsut0CH2g4XWbfLtmlCkXmtmdcDCxNB+mQcu1w/1+L/VD7vi/PSv7X2JYV7SCcR+jiPXnQtA==",
+ "engines": {
+ "node": ">= 0.4"
+ }
},
- "media-typer": {
+ "node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
- "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "engines": {
+ "node": ">= 0.6"
+ }
},
- "merge-descriptors": {
+ "node_modules/merge-descriptors": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
- "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="
+ "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "methods": {
+ "node_modules/methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
- "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+ "engines": {
+ "node": ">= 0.6"
+ }
},
- "mime": {
+ "node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
- "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
},
- "mime-db": {
+ "node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
},
- "mime-types": {
+ "node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "requires": {
+ "dependencies": {
"mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
}
},
- "morgan": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",
- "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==",
- "requires": {
+ "node_modules/morgan": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz",
+ "integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==",
+ "license": "MIT",
+ "dependencies": {
"basic-auth": "~2.0.1",
"debug": "2.6.9",
"depd": "~2.0.0",
"on-finished": "~2.3.0",
- "on-headers": "~1.0.2"
+ "on-headers": "~1.1.0"
},
- "dependencies": {
- "depd": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
- "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
- }
+ "engines": {
+ "node": ">= 0.8.0"
}
},
- "ms": {
+ "node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
- "negotiator": {
+ "node_modules/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
- "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
},
- "object-inspect": {
+ "node_modules/object-inspect": {
"version": "1.13.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz",
- "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA=="
+ "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
- "on-finished": {
+ "node_modules/on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
- "requires": {
+ "dependencies": {
"ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
}
},
- "on-headers": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
- "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
+ "node_modules/on-headers": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
+ "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
},
- "oracledb": {
+ "node_modules/oracledb": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/oracledb/-/oracledb-5.1.0.tgz",
- "integrity": "sha512-/IpzG729lFj8NBsA0xaOijdf6ZO3VuJbebma/hSf+VUZoIyzI/mCzBX047ramb/W8yJ1SV/HdWgs+XEczbqsXg=="
+ "integrity": "sha512-/IpzG729lFj8NBsA0xaOijdf6ZO3VuJbebma/hSf+VUZoIyzI/mCzBX047ramb/W8yJ1SV/HdWgs+XEczbqsXg==",
+ "deprecated": "Outdated package",
+ "hasInstallScript": true,
+ "engines": {
+ "node": ">=10.16"
+ }
},
- "parseurl": {
+ "node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
- "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "engines": {
+ "node": ">= 0.8"
+ }
},
- "path-to-regexp": {
+ "node_modules/path-to-regexp": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
},
- "proxy-addr": {
+ "node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
- "requires": {
+ "dependencies": {
"forwarded": "0.2.0",
"ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
}
},
- "qs": {
+ "node_modules/qs": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
- "requires": {
+ "dependencies": {
"side-channel": "^1.0.6"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "range-parser": {
+ "node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
- "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
},
- "raw-body": {
+ "node_modules/raw-body": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
- "requires": {
+ "dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
}
},
- "safe-buffer": {
+ "node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
- "safer-buffer": {
+ "node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
- "send": {
+ "node_modules/send": {
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
- "requires": {
+ "dependencies": {
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
@@ -493,120 +696,172 @@
"range-parser": "~1.2.1",
"statuses": "2.0.1"
},
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/send/node_modules/encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/send/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+ },
+ "node_modules/send/node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"dependencies": {
- "encodeurl": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
- "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
- },
- "ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
- },
- "on-finished": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
- "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
- "requires": {
- "ee-first": "1.1.1"
- }
- }
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
}
},
- "serve-static": {
+ "node_modules/serve-static": {
"version": "1.16.2",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
- "requires": {
+ "dependencies": {
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.19.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
}
},
- "setprototypeof": {
+ "node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
- "side-channel": {
+ "node_modules/side-channel": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
- "requires": {
+ "dependencies": {
"es-errors": "^1.3.0",
"object-inspect": "^1.13.3",
"side-channel-list": "^1.0.0",
"side-channel-map": "^1.0.1",
"side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "side-channel-list": {
+ "node_modules/side-channel-list": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
- "requires": {
+ "dependencies": {
"es-errors": "^1.3.0",
"object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "side-channel-map": {
+ "node_modules/side-channel-map": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
- "requires": {
+ "dependencies": {
"call-bound": "^1.0.2",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.5",
"object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "side-channel-weakmap": {
+ "node_modules/side-channel-weakmap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
- "requires": {
+ "dependencies": {
"call-bound": "^1.0.2",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.5",
"object-inspect": "^1.13.3",
"side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "statuses": {
+ "node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
- "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "engines": {
+ "node": ">= 0.8"
+ }
},
- "toidentifier": {
+ "node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
- "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "engines": {
+ "node": ">=0.6"
+ }
},
- "type-is": {
+ "node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
- "requires": {
+ "dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
}
},
- "unpipe": {
+ "node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
- "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "engines": {
+ "node": ">= 0.8"
+ }
},
- "utils-merge": {
+ "node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
- "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
},
- "vary": {
+ "node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
- "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "engines": {
+ "node": ">= 0.8"
+ }
}
}
}
diff --git a/javascript/rest-api/part-2-database-basics/hr_app/package.json b/javascript/rest-api/part-2-database-basics/hr_app/package.json
index 136a672d..80632a4a 100644
--- a/javascript/rest-api/part-2-database-basics/hr_app/package.json
+++ b/javascript/rest-api/part-2-database-basics/hr_app/package.json
@@ -10,7 +10,7 @@
"license": "Apache-2.0",
"dependencies": {
"express": "^4.21.2",
- "morgan": "^1.10.0",
+ "morgan": "^1.10.1",
"oracledb": "^5.1.0"
}
}
diff --git a/precompilers/Readme.md b/precompilers/Readme.md
new file mode 100644
index 00000000..d2ee4bc2
--- /dev/null
+++ b/precompilers/Readme.md
@@ -0,0 +1,41 @@
+# Precompiler Examples
+
+This directory contains sample programs illustrating the use of Oracle Precompilers (Pro*C and Pro*COBOL).
+It also includes makefiles for building and compiling these samples into executables.
+
+## Prerequisites
+
+### Oracle Packages / Components
+
+If you are using the Oracle Instant Client, install the following packages for your platform:
+
+- **Basic**
+- **SDK**
+- **Precompiler**
+
+### Compilers
+
+- A **C/C++ compiler** is required for the Pro*C samples.
+- A **COBOL compiler** is required for the Pro*COBOL samples.
+
+### Minimum Required Version
+
+The minimum required **Oracle Database** and **Oracle Client** versions are **19c** in both cases.
+
+## Included Files
+
+| File Name | Description |
+|-----------------------|-----------------------------------------------------------------------------------------------|
+| [`procdemo.pc`](./procdemo.pc) | Sample Pro*C program demonstrating basic database operations using Oracle Precompiler. |
+| [`procobdemo.pco`](./procobdemo.pco) | Sample Pro*COBOL program illustrating database interaction using Oracle Precompiler. |
+| [`makefile_proc.mk`](./makefile_proc.mk) | Makefile for compiling the Pro*C example, managing dependencies, and generating the executable. |
+| [`makefile_procob.mk`](./makefile_procob.mk) | Makefile for compiling the Pro*COBOL example, handling build processes, and producing the executable. |
+
+## How to Compile and Run
+
+To build the sample programs:
+
+- Use `make -f makefile_proc.mk` for **Pro*C** samples.
+- Use `make -f makefile_procob.mk` for **Pro*COBOL** samples.
+
+Refer to the respective makefiles for more detailed instructions on compilation and execution steps.
diff --git a/precompilers/makefile_proc.mk b/precompilers/makefile_proc.mk
new file mode 100644
index 00000000..9223fe99
--- /dev/null
+++ b/precompilers/makefile_proc.mk
@@ -0,0 +1,123 @@
+###############################################################################
+# Make file for PROC demos
+###############################################################################
+# Usage :
+# For compiling proc demos
+# make -f makefile_proc.mk
+#
+# For precompiling, compiling & linking the procdemo.pc file
+# make -f makefile_proc.mk build EXE=procdemo OBJS=procdemo.o
+#
+# In general, for any proc program
+# make -f makefile_proc.mk build EXE= OBJS=""
+#
+# To make use of any PROC options during precompilation,
+# make -f makefile_proc.mk build PROCFLAGS=""
+# EXE= OBJS=""
+#
+# NOTES:
+# 1. Please change "cc/CC" and the "InstantClient directories" to point to
+# appropiate locations on your machine before using this makefile.
+# 2. In case of RPM installation, please change the following variables
+# as mentioned below:
+# PROC=/usr/lib/oracle/VV.v/client/bin/proc
+# CCINCLUDES=$(I_SYM)/usr/include/oracle/VV.v/client
+# PRECOMPPUBH=/usr/include/oracle/VV.v/client
+# ICLIBHOME=/usr/lib/oracle/VV.v/client/lib/
+# Legend:
+# VV - Major Oracle version number
+# v - Minor Oracle version number
+# (Ex: For the release 11.2, VV = 11 and v = 2)
+#
+###############################################################################
+
+
+CC=/usr/bin/gcc
+cc=/usr/bin/gcc
+
+# InstantClient Directories.
+ICSDKHOME=../
+ICLIBHOME=../../
+
+MKLINK=ln
+REMOVE=rm -rf
+CLNCACHE=cleancache
+CACHEDIR=SunWS_cachea
+MAKE=make
+MAKEFILE=makefile_proc.mk
+PROCDEMO=procdemo
+
+PROC=$(ICSDKHOME)/proc
+SO_EXT=.so
+I_SYM=-I
+
+CCINCLUDES= $(I_SYM)$(ICSDKHOME)/include
+
+# Pre-compiler Flags.
+PRECOMPPUBH=$(ICSDKHOME)include
+
+# Compiler Flags.
+OPTIMIZE=-O2
+LDPATHFLAG=-L
+SPFLAGS=-DLINUX -D_GNU_SOURCE -D_LARGEFILE64_SOURCE=1 -D_LARGEFILE_SOURCE=1 -DSLTS_ENABLE -DSLMXMX_ENABLE -D_REENTRANT -DNS_THREADS
+CCFLAGS= -fPIC -DPRECOMP
+LDFLAGS=-g
+LPFLAGS=
+GFLAG=
+CDEBUG=
+USRFLAGS=
+ICLIBPATH=$(LDPATHFLAG)$(ICLIBHOME)
+PFLAGS=$(CCINCLUDES) $(SPFLAGS) $(LPFLAGS)
+CFLAGS=$(GFLAG) $(OPTIMIZE) $(CDEBUG) $(CCFLAGS) $(PFLAGS) $(USRFLAGS)
+
+# Libraries.
+PROLDLIBS=$(LDCLIENTLIBS) $(THREADLIBS)
+LDCLIENTLIBS=$(ICLIBPATH) $(LLIBCLNTSH) $(LDLIBS)
+LLIBCLNTSH=$(LDLIBFLAG)$(LIBCLNTSHNAME)
+LDLIBFLAG=-l
+LIBCLNTCORENAME=clntshcore
+LIBCLNTSHNAME=clntsh
+LDLIBS=$(EXSYSLIBS) $(MATHLIB) $(USRLIBS)
+EXSYSLIBS=-ldl
+MATHLIB=-lm
+THREADLIBS=-lpthread
+
+C2O=$(CC) $(CFLAGS) -c $*.c
+PCC2C=$(PROC) $(PROCFLAGS) iname=$(PCCSRC)
+DEMO_PROC_BUILD=$(CC) -o $(EXE) $(OBJS) $(LDFLAGS) $(PROLDLIBS)
+
+#-----------------------------------------------------------------------------
+# Targets for building the proc sample programs.
+all: clean $(PROCDEMO)
+
+$(PROCDEMO):
+ $(MAKE) -f $(MAKEFILE) build OBJS=$@.o EXE=$@
+
+build: $(CLNCACHE) $(OBJS)
+ $(DEMO_PROC_BUILD)
+
+#-----------------------------------------------------------------------------
+# Here are some rules for converting .pc -> .c -> .o
+.SUFFIXES: .pc .c .o
+
+pc1:
+ $(PCC2C)
+
+.pc.c:
+ $(MAKE) -f $(MAKEFILE) PROCFLAGS="$(PROCFLAGS)" PCCSRC=$* I_SYM=include= pc1
+
+.pc.o:
+ $(MAKE) -f $(MAKEFILE) PROCFLAGS="$(PROCFLAGS)" PCCSRC=$* I_SYM=include= pc1
+ $(C2O)
+
+.c.o:
+ $(C2O)
+
+#-----------------------------------------------------------------------------
+# Clean up all executables, *.o and generated *.c files
+clean: $(CLNCACHE)
+ $(REMOVE) $(PROCDEMO) $(PROCDEMO).o $(PROCDEMO).c $(PROCDEMO).lis
+
+cleancache:
+ $(REMOVE) $(CACHEDIR)
+
diff --git a/precompilers/makefile_procob.mk b/precompilers/makefile_procob.mk
new file mode 100644
index 00000000..e4eeb493
--- /dev/null
+++ b/precompilers/makefile_procob.mk
@@ -0,0 +1,96 @@
+###############################################################################
+# Make file for PROCOB demos
+###############################################################################
+# Usage :
+# For compiling procob demos
+# make -f makefile_procob.mk
+#
+# For precompiling, compiling & linking the procobdemo.pco file
+# make -f makefile_procob.mk build EXE=procobdemo COBS=procobdemo.cob
+#
+# In general, for any procob program
+# make -f makefile_procob.mk build EXE= COBS=""
+# To make use of any PROCOB options during precompilation,
+# make -f makefile_procob.mk build PROCOBFLAGS=""
+# EXE= COBS=""
+#
+# NOTES:
+# 1. Please change "COB" and the "InstantClient directories" to point to
+# appropiate locations on your machine before using this makefile.
+# 2. In case of RPM installation, please change the following variables
+# as mentioned below:
+# PROCOB=/usr/lib/oracle/VV.v/client/bin/procob
+# ICLIBHOME=/usr/lib/oracle/VV.v/client/lib/
+# Legend:
+# VV - Major Oracle version number
+# v - Minor Oracle version number
+# (Ex: For the release 18.1, VV = 12 and v = 1)
+#
+###############################################################################
+
+COB=cob
+
+# InstantClient Directories.
+ICSDKHOME=../
+ICLIBHOME=../../
+
+MKLINK=ln
+REMOVE=rm -rf
+CLNCACHE=cleancache
+CACHEDIR=SunWS_cachea
+MAKE=make
+MAKEFILE=makefile_procob.mk
+PROCOBDEMO=procobdemo
+
+PROCOB=$(ICSDKHOME)/procob
+ICLIBPATH=$(LDPATHFLAG)$(ICLIBHOME)
+SO_EXT=.so
+COBFLAGS=-C IBMCOMP -C NESTCALL -t -x
+LDPATHFLAG=-L
+COBSQLINTF=$(ICLIBHOME)cobsqlintf.o
+LDLIBS=$(EXSYSLIBS) $(MATHLIB) $(USRLIBS)
+EXSYSLIBS=-ldl
+MATHLIB=-lm
+COBOL_PROLDLIBS=$(SHARED_CLIENTLIBS) $(LDLIBS)
+SHARED_CLIENTLIBS=$(LLIBCLNTSH) $(LDFLAGS)
+LLIBCLNTSH=$(LDLIBFLAG)$(LIBCLNTSHNAME)
+LDLIBFLAG=-l
+LIBCLNTCORENAME=clntshcore
+LIBCLNTSHNAME=clntsh
+LDFLAGS=-g
+
+DEMO_PROCOB_BUILD=$(COB) $(COBFLAGS) -o $(EXE) $(COBS) $(ICLIBPATH) $(COBSQLINTF) $(COBOL_PROLDLIBS)
+
+#-----------------------------------------------------------------------------
+# Targets for building the procob sample programs.
+#
+# The target 'build' puts together an executable $(EXE) from the cobol
+# sources in $(COBS) and the libraries in $(COBOL_PROLDLIBS).
+# The rules to make .cob files from .pco files are later in this file.
+#
+all: clean $(PROCOBDEMO)
+
+$(PROCOBDEMO):
+ $(MAKE) -f $(MAKEFILE) build COBS=$@.cob EXE=$@
+
+build: $(CLNCACHE) $(COBS)
+ $(DEMO_PROCOB_BUILD)
+
+#-----------------------------------------------------------------------------
+# Here are some rules for converting .pco -> .cob -> .o and for .cob -> .gnt.
+#
+.SUFFIXES: .cob .cbl .o .pco $(GNT)
+
+.pco.cob:
+ $(PROCOB) $(PROCOBFLAGS) iname=$*.pco
+
+.cob$(GNT):
+ $(COB2GNT)
+
+#-----------------------------------------------------------------------------
+# Clean up all executables, *.o and generated *.cob files
+clean: $(CLNCACHE)
+ $(REMOVE) $(PROCOBDEMO) $(PROCOBDEMO).o $(PROCOBDEMO).cob $(PROCDEMO).lis $(PROCOBDEMO).int $(PROCOBDEMO).idy
+
+cleancache:
+ $(REMOVE) $(CACHEDIR)
diff --git a/precompilers/procdemo.pc b/precompilers/procdemo.pc
new file mode 100644
index 00000000..a2f35ff4
--- /dev/null
+++ b/precompilers/procdemo.pc
@@ -0,0 +1,125 @@
+/* Copyright (c) 2025, Oracle and/or its affiliates.*/
+/* All rights reserved.*/
+
+/* NAME
+ * procdemo.pc - Pro*C demo program
+ *
+ * DESCRIPTION
+ * This program connects to ORACLE, declares and opens a cursor,
+ * fetches the names, salaries, and commissions of all
+ * salespeople, displays the results, then closes the cursor.
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define UNAME_PWD_LEN 256
+
+/*
+ * Use the precompiler typedef'ing capability to create
+ * null-terminated strings for the authentication host
+ * variables. (This isn't really necessary--plain char *'s
+ * would work as well. This is just for illustration.)
+ */
+typedef char asciiz[UNAME_PWD_LEN];
+
+EXEC SQL TYPE asciiz IS CHARZ(UNAME_PWD_LEN) REFERENCE;
+asciiz username;
+asciiz password;
+
+struct emp_info
+{
+ asciiz emp_name;
+ float salary;
+ float commission;
+};
+
+void sql_error(msg)
+ char *msg;
+{
+ char err_msg[512];
+ size_t buf_len, msg_len;
+
+ EXEC SQL WHENEVER SQLERROR CONTINUE;
+
+ printf("\n%s\n", msg);
+
+/* Call sqlglm() to get the complete text of the
+ * error message.
+ */
+ buf_len = sizeof (err_msg);
+ sqlglm(err_msg, &buf_len, &msg_len);
+ printf("%.*s\n", msg_len, err_msg);
+
+ EXEC SQL ROLLBACK RELEASE;
+ exit(EXIT_FAILURE);
+}
+
+void main()
+{
+ struct emp_info *emp_rec_ptr;
+
+/* Allocate memory for emp_info struct. */
+ if ((emp_rec_ptr =
+ (struct emp_info *) malloc(sizeof(struct emp_info))) == 0)
+ {
+ fprintf(stderr, "Memory allocation error.\n");
+ exit(EXIT_FAILURE);
+ }
+
+/* application user to set the database credentials here */
+/* CAUTION: Username and password buffers are 256 characters. */
+/* Do not paste credentials longer than 256 characters. */
+
+ strcpy(username, "");
+ strcpy(password, "");
+
+ EXEC SQL WHENEVER SQLERROR DO sql_error("ORACLE error--");
+
+ EXEC SQL CONNECT :username IDENTIFIED BY :password;
+ printf("\nConnected to ORACLE as user: %s\n", username);
+
+/* Declare the cursor. All static SQL explicit cursors
+ * contain SELECT commands. 'salespeople' is a SQL identifier,
+ * not a (C) host variable.
+ */
+ EXEC SQL DECLARE salespeople CURSOR FOR
+ SELECT ENAME, SAL, COMM
+ FROM EMP
+ WHERE JOB LIKE 'SALES%';
+
+/* Open the cursor. */
+ EXEC SQL OPEN salespeople;
+
+/* Get ready to print results. */
+ printf("\n\nThe company's salespeople are--\n\n");
+ printf("Salesperson Salary Commission\n");
+ printf("----------- ------ ----------\n");
+
+/* Loop, fetching all salesperson's statistics.
+ * Cause the program to break the loop when no more
+ * data can be retrieved on the cursor.
+ */
+ EXEC SQL WHENEVER NOT FOUND DO break;
+
+ for (;;)
+ {
+ EXEC SQL FETCH salespeople INTO :emp_rec_ptr;
+ printf("%s %9.2f %12.2f\n", emp_rec_ptr->emp_name,
+ emp_rec_ptr->salary, emp_rec_ptr->commission);
+ }
+
+/* Close the cursor. */
+ EXEC SQL CLOSE salespeople;
+
+ printf("\nGOOD-BYE!!\n\n");
+
+ EXEC SQL COMMIT WORK RELEASE;
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/precompilers/procobdemo.pco b/precompilers/procobdemo.pco
new file mode 100644
index 00000000..5e947b6f
--- /dev/null
+++ b/precompilers/procobdemo.pco
@@ -0,0 +1,99 @@
+ *****************************************************************
+ * Copyright (c) 2025, Oracle and/or its affiliates.. *
+ * All rights reserved. *
+ * *
+ * procobdemo.pco - Pro*COBOL demo file. *
+ * *
+ * DESCRIPTION: *
+ * This program logs on to ORACLE, declares and opens a cursor, *
+ * fetches the names, salaries, and commissions of all *
+ * salespeople, displays the results, then closes the cursor. *
+ *****************************************************************
+
+ IDENTIFICATION DIVISION.
+ PROGRAM-ID. CURSOR-OPS.
+ ENVIRONMENT DIVISION.
+ DATA DIVISION.
+ WORKING-STORAGE SECTION.
+
+ EXEC SQL BEGIN DECLARE SECTION END-EXEC.
+ 01 USERNAME PIC X(256) VARYING.
+ 01 PASSWD PIC X(256) VARYING.
+ 01 EMP-REC-VARS.
+ 05 EMP-NAME PIC X(10) VARYING.
+ 05 SALARY PIC S9(6)V99
+ DISPLAY SIGN LEADING SEPARATE.
+ 05 COMMISSION PIC S9(6)V99
+ DISPLAY SIGN LEADING SEPARATE.
+ EXEC SQL VAR SALARY IS DISPLAY(8,2) END-EXEC.
+ EXEC SQL VAR COMMISSION IS DISPLAY(8,2) END-EXEC.
+ EXEC SQL END DECLARE SECTION END-EXEC.
+
+ EXEC SQL INCLUDE SQLCA END-EXEC.
+
+ 01 DISPLAY-VARIABLES.
+ 05 D-EMP-NAME PIC X(10).
+ 05 D-SALARY PIC Z(4)9.99.
+ 05 D-COMMISSION PIC Z(4)9.99.
+
+ PROCEDURE DIVISION.
+
+ BEGIN-PGM.
+ EXEC SQL WHENEVER SQLERROR
+ DO PERFORM SQL-ERROR END-EXEC.
+ PERFORM LOGON.
+ EXEC SQL DECLARE SALESPEOPLE CURSOR FOR
+ SELECT ENAME, SAL, COMM
+ FROM EMP
+ WHERE JOB LIKE 'SALES%'
+ END-EXEC.
+ EXEC SQL OPEN SALESPEOPLE END-EXEC.
+ DISPLAY " ".
+ DISPLAY "SALESPERSON SALARY COMMISSION".
+ DISPLAY "----------- ---------- ----------".
+
+ FETCH-LOOP.
+ EXEC SQL WHENEVER NOT FOUND
+ DO PERFORM SIGN-OFF END-EXEC.
+ EXEC SQL FETCH SALESPEOPLE
+ INTO :EMP-NAME, :SALARY, :COMMISSION
+ END-EXEC.
+ MOVE EMP-NAME-ARR TO D-EMP-NAME.
+ MOVE SALARY TO D-SALARY.
+ MOVE COMMISSION TO D-COMMISSION.
+ DISPLAY D-EMP-NAME, " ", D-SALARY, " ", D-COMMISSION.
+ MOVE SPACES TO EMP-NAME-ARR.
+ GO TO FETCH-LOOP.
+
+ LOGON.
+ *****************************************************************
+ * Application user: Set the database credentials here. *
+ * CAUTION: Username and password buffers are 256 bytes. *
+ * Do not paste credentials longer than 256 characters. *
+ *****************************************************************
+ MOVE "" TO USERNAME-ARR.
+ MOVE LENGTH OF "" TO USERNAME-LEN.
+ MOVE "" TO PASSWD-ARR.
+ MOVE LENGTH OF "" TO PASSWD-LEN.
+ EXEC SQL
+ CONNECT :USERNAME IDENTIFIED BY :PASSWD
+ END-EXEC.
+ DISPLAY " ".
+ DISPLAY "CONNECTED TO ORACLE AS USER: ", USERNAME-ARR.
+
+ SIGN-OFF.
+ EXEC SQL CLOSE SALESPEOPLE END-EXEC.
+ DISPLAY " ".
+ DISPLAY "HAVE A GOOD DAY.".
+ DISPLAY " ".
+ EXEC SQL COMMIT WORK RELEASE END-EXEC.
+ STOP RUN.
+
+ SQL-ERROR.
+ EXEC SQL WHENEVER SQLERROR CONTINUE END-EXEC.
+ DISPLAY " ".
+ DISPLAY "ORACLE ERROR DETECTED:".
+ DISPLAY " ".
+ DISPLAY SQLERRMC.
+ EXEC SQL ROLLBACK WORK RELEASE END-EXEC.
+ STOP RUN.
diff --git a/python/python-oracledb/README.md b/python/python-oracledb/README.md
index 380ce058..d96a450e 100644
--- a/python/python-oracledb/README.md
+++ b/python/python-oracledb/README.md
@@ -29,8 +29,9 @@ Oracle Database.
### Examples in a Container
-The [sample_container](./sample_container) directory has a Dockerfile that will
-build a container with the samples and a running Oracle Database.
+The [containers/samples_and_db](./containers/samples_and_db) directory has a
+Dockerfile for building a container with the samples and a running Oracle
+Database.
### Notebooks
diff --git a/python/python-oracledb/async_gather.py b/python/python-oracledb/async_gather.py
index 78b12937..81f45487 100644
--- a/python/python-oracledb/async_gather.py
+++ b/python/python-oracledb/async_gather.py
@@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
-# Copyright (c) 2023, 2024, Oracle and/or its affiliates.
+# Copyright (c) 2023, 2025, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@@ -27,8 +27,13 @@
#
# Demonstrates using a connection pool with asyncio and gather().
#
-# Multiple database sessions will be opened and used by each coroutine. The
-# number of connections opened can depend on the speed of your environment.
+# This also shows the use of pool_alias for connection pool caching, so the
+# pool handle does not need to passed through the app.
+#
+# Each coroutine invocation will acquire a connection from the connection pool.
+# The number of connections in the pool will depend on the speed of your
+# environment. In some cases existing connections will get reused. In other
+# cases up to CONCURRENCY connections will be created by the pool.
# -----------------------------------------------------------------------------
import asyncio
@@ -36,33 +41,38 @@
import oracledb
import sample_env
+# Pool name for the connection pool cache
+POOL_ALIAS_NAME = "mypool"
+
# Number of coroutines to run
CONCURRENCY = 5
# Query the unique session identifier/serial number combination of a connection
-SQL = """SELECT UNIQUE CURRENT_TIMESTAMP AS CT, sid||'-'||serial# AS SIDSER
- FROM v$session_connect_info
- WHERE sid = SYS_CONTEXT('USERENV', 'SID')"""
+SQL = """select unique current_timestamp as ct, sid||'-'||serial# as sidser
+ from v$session_connect_info
+ where sid = sys_context('userenv', 'sid')"""
# Show the unique session identifier/serial number of each connection that the
-# pool opens
+# pool creates
async def init_session(connection, requested_tag):
res = await connection.fetchone(SQL)
print(res[0].strftime("%H:%M:%S.%f"), "- init_session SID-SERIAL#", res[1])
# The coroutine simply shows the session identifier/serial number of the
-# connection returned by the pool.acquire() call
-async def query(pool):
- async with pool.acquire() as connection:
+# connection returned from the pool
+async def query():
+ async with oracledb.connect_async(
+ pool_alias=POOL_ALIAS_NAME
+ ) as connection:
await connection.callproc("dbms_session.sleep", [1])
res = await connection.fetchone(SQL)
print(res[0].strftime("%H:%M:%S.%f"), "- query SID-SERIAL#", res[1])
async def main():
- pool = oracledb.create_pool_async(
+ oracledb.create_pool_async(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
@@ -70,12 +80,14 @@ async def main():
min=1,
max=CONCURRENCY,
session_callback=init_session,
+ pool_alias=POOL_ALIAS_NAME,
)
- coroutines = [query(pool) for i in range(CONCURRENCY)]
+ coroutines = [query() for i in range(CONCURRENCY)]
await asyncio.gather(*coroutines)
+ pool = oracledb.get_pool(POOL_ALIAS_NAME)
await pool.close()
diff --git a/python/python-oracledb/bind_insert.py b/python/python-oracledb/bind_insert.py
index abd7750f..712e858e 100644
--- a/python/python-oracledb/bind_insert.py
+++ b/python/python-oracledb/bind_insert.py
@@ -86,7 +86,7 @@
# Inserting a single bind still needs tuples
# -----------------------------------------------------------------------------
-rows = [("Eleventh",), ("Twelth",)]
+rows = [("Eleventh",), ("Twelfth",)]
with connection.cursor() as cursor:
cursor.executemany("insert into mytab(id, data) values (12, :1)", rows)
diff --git a/python/python-oracledb/bind_insert_async.py b/python/python-oracledb/bind_insert_async.py
index 2e3a3660..37f0acfb 100644
--- a/python/python-oracledb/bind_insert_async.py
+++ b/python/python-oracledb/bind_insert_async.py
@@ -92,7 +92,7 @@ async def main():
# Inserting a single bind still needs tuples
# -------------------------------------------------------------------------
- rows = [("Eleventh",), ("Twelth",)]
+ rows = [("Eleventh",), ("Twelfth",)]
await connection.executemany(
"insert into mytab(id, data) values (12, :1)", rows
diff --git a/python/python-oracledb/bulk_aq.py b/python/python-oracledb/bulk_aq.py
index 7c0a1e40..8ea983b1 100644
--- a/python/python-oracledb/bulk_aq.py
+++ b/python/python-oracledb/bulk_aq.py
@@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
-# Copyright (c) 2019, 2023, Oracle and/or its affiliates.
+# Copyright (c) 2019, 2025, Oracle and/or its affiliates.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@@ -53,8 +53,9 @@
"The twelfth and final message",
]
-# this script is currently only supported in python-oracledb thick mode
-oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
+# determine whether to use python-oracledb thin mode or thick mode
+if not sample_env.get_is_thin():
+ oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
# connect to database
connection = oracledb.connect(
@@ -64,39 +65,36 @@
)
# create a queue
-with connection.cursor() as cursor:
- queue = connection.queue(QUEUE_NAME)
- queue.deqoptions.wait = oracledb.DEQ_NO_WAIT
- queue.deqoptions.navigation = oracledb.DEQ_FIRST_MSG
+queue = connection.queue(QUEUE_NAME)
+queue.deqoptions.wait = oracledb.DEQ_NO_WAIT
+queue.deqoptions.navigation = oracledb.DEQ_FIRST_MSG
- # dequeue all existing messages to ensure the queue is empty, just so that
- # the results are consistent
- while queue.deqone():
- pass
+# dequeue all existing messages to ensure the queue is empty, just so that
+# the results are consistent
+while queue.deqone():
+ pass
# enqueue a few messages
-with connection.cursor() as cursor:
- print("Enqueuing messages...")
- batch_size = 6
- data_to_enqueue = PAYLOAD_DATA
- while data_to_enqueue:
- batch_data = data_to_enqueue[:batch_size]
- data_to_enqueue = data_to_enqueue[batch_size:]
- messages = [connection.msgproperties(payload=d) for d in batch_data]
- for data in batch_data:
- print(data)
- queue.enqmany(messages)
- connection.commit()
+print("Enqueuing messages...")
+batch_size = 6
+data_to_enqueue = PAYLOAD_DATA
+while data_to_enqueue:
+ batch_data = data_to_enqueue[:batch_size]
+ data_to_enqueue = data_to_enqueue[batch_size:]
+ messages = [connection.msgproperties(payload=d) for d in batch_data]
+ for data in batch_data:
+ print(data)
+ queue.enqmany(messages)
+connection.commit()
# dequeue the messages
-with connection.cursor() as cursor:
- print("\nDequeuing messages...")
- batch_size = 8
- while True:
- messages = queue.deqmany(batch_size)
- if not messages:
- break
- for props in messages:
- print(props.payload.decode())
- connection.commit()
- print("\nDone.")
+print("\nDequeuing messages...")
+batch_size = 8
+while True:
+ messages = queue.deqmany(batch_size)
+ if not messages:
+ break
+ for props in messages:
+ print(props.payload.decode())
+connection.commit()
+print("\nDone.")
diff --git a/python/python-oracledb/bulk_aq_async.py b/python/python-oracledb/bulk_aq_async.py
new file mode 100644
index 00000000..539a6206
--- /dev/null
+++ b/python/python-oracledb/bulk_aq_async.py
@@ -0,0 +1,107 @@
+# -----------------------------------------------------------------------------
+# Copyright (c) 2025, Oracle and/or its affiliates.
+#
+# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
+#
+# Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
+# Canada. All rights reserved.
+#
+# This software is dual-licensed to you under the Universal Permissive License
+# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
+# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
+# either license.
+#
+# If you elect to accept the software under the Apache License, Version 2.0,
+# the following applies:
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# bulk_aq_async.py
+#
+# Demonstrates how to use bulk enqueuing and dequeuing of messages with
+# advanced queuing using asyncio. It makes use of a RAW queue created in the
+# sample setup.
+# -----------------------------------------------------------------------------
+
+import asyncio
+
+import oracledb
+import sample_env
+
+QUEUE_NAME = "DEMO_RAW_QUEUE"
+PAYLOAD_DATA = [
+ "The first message",
+ "The second message",
+ "The third message",
+ "The fourth message",
+ "The fifth message",
+ "The sixth message",
+ "The seventh message",
+ "The eighth message",
+ "The ninth message",
+ "The tenth message",
+ "The eleventh message",
+ "The twelfth and final message",
+]
+
+
+async def main():
+
+ # connect to database
+ async with oracledb.connect_async(
+ user=sample_env.get_main_user(),
+ password=sample_env.get_main_password(),
+ dsn=sample_env.get_connect_string(),
+ ) as connection:
+
+ # create a queue
+ queue = connection.queue(QUEUE_NAME)
+ queue.deqoptions.wait = oracledb.DEQ_NO_WAIT
+ queue.deqoptions.navigation = oracledb.DEQ_FIRST_MSG
+
+ # dequeue all existing messages to ensure the queue is empty, just so
+ # that the results are consistent
+ while await queue.deqone():
+ pass
+
+ # enqueue a few messages
+ print("Enqueuing messages...")
+ batch_size = 6
+ data_to_enqueue = PAYLOAD_DATA
+ while data_to_enqueue:
+ batch_data = data_to_enqueue[:batch_size]
+ data_to_enqueue = data_to_enqueue[batch_size:]
+ messages = [
+ connection.msgproperties(payload=d) for d in batch_data
+ ]
+ for data in batch_data:
+ print(data)
+ await queue.enqmany(messages)
+ await connection.commit()
+
+ # dequeue the messages
+ print("\nDequeuing messages...")
+ batch_size = 8
+ while True:
+ messages = await queue.deqmany(batch_size)
+ if not messages:
+ break
+ for props in messages:
+ print(props.payload.decode())
+ await connection.commit()
+ print("\nDone.")
+
+
+asyncio.run(main())
diff --git a/python/python-oracledb/connection_pool.py b/python/python-oracledb/connection_pool.py
index dd76c05e..6a7647ea 100644
--- a/python/python-oracledb/connection_pool.py
+++ b/python/python-oracledb/connection_pool.py
@@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
-# Copyright (c) 2022, 2024, Oracle and/or its affiliates.
+# Copyright (c) 2022, 2025, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@@ -27,6 +27,9 @@
#
# Demonstrates the use of connection pooling using a Flask web application.
#
+# This also shows the use of pool_alias for connection pool caching, so the
+# pool handle does not need to passed through the app.
+#
# Connection Pools can significantly reduce connection times for long running
# applications that repeatedly open and close connections. Connection pools
# allow multiple, concurrent web requests to be efficiently handled. Internal
@@ -37,7 +40,7 @@
#
# 1. Install Flask, for example like:
#
-# python -m pip install Flask
+# python -m pip install flask
#
# 2. (Optional) Set environment variables referenced in sample_env.py
#
@@ -67,37 +70,42 @@
import sample_env
# Port to listen on
-port = int(os.environ.get("PORT", "8080"))
+PORT = int(os.environ.get("PORT", "8080"))
+
+# Generally a fixed-size pool is recommended, i.e. POOL_MIN=POOL_MAX. Here
+# the pool contains 4 connections, which will allow 4 concurrent users.
+POOL_MIN = 4
+POOL_MAX = 4
+POOL_INC = 0
+
+# Pool name for the connection pool cache
+POOL_ALIAS_NAME = "mypool"
# determine whether to use python-oracledb thin mode or thick mode
if not sample_env.get_is_thin():
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
+
# -----------------------------------------------------------------------------
# start_pool(): starts the connection pool
+#
+# The pool is stored in the pool cache. Connections can later be acquired from
+# the pool by passing the same pool_alias value to oracledb.connect()
def start_pool():
- # Generally a fixed-size pool is recommended, i.e. pool_min=pool_max. Here
- # the pool contains 4 connections, which will allow 4 concurrent users.
-
- pool_min = 4
- pool_max = 4
- pool_inc = 0
-
- pool = oracledb.create_pool(
+ oracledb.create_pool(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
params=sample_env.get_pool_params(),
- min=pool_min,
- max=pool_max,
- increment=pool_inc,
+ min=POOL_MIN,
+ max=POOL_MAX,
+ increment=POOL_INC,
session_callback=init_session,
+ pool_alias=POOL_ALIAS_NAME,
)
- return pool
-
# init_session(): a 'session callback' to efficiently set any initial state
# that each connection should have.
@@ -105,9 +113,9 @@ def start_pool():
# This particular demo doesn't use dates, so sessionCallback could be omitted,
# but it does show the kinds of settings many apps would use.
#
-# If you have multiple SQL statements, then call them all in a PL/SQL anonymous
-# block with BEGIN/END so you only use execute() once. This is shown later in
-# create_schema().
+# If you have multiple SQL statements, an optimization is to call them all in a
+# PL/SQL anonymous block with BEGIN/END so you only use cursor.execute() once.
+# This is shown later in create_schema().
#
def init_session(connection, requestedTag_ignored):
with connection.cursor() as cursor:
@@ -125,7 +133,7 @@ def init_session(connection, requestedTag_ignored):
# create_schema(): drop and create the demo table, and add a row
def create_schema():
- with pool.acquire() as connection:
+ with oracledb.connect(pool_alias=POOL_ALIAS_NAME) as connection:
with connection.cursor() as cursor:
cursor.execute(
"""
@@ -169,7 +177,7 @@ def index():
# variable 'idbv'.
@app.route("/post/")
def post(username):
- with pool.acquire() as connection:
+ with oracledb.connect(pool_alias=POOL_ALIAS_NAME) as connection:
with connection.cursor() as cursor:
connection.autocommit = True
idbv = cursor.var(int)
@@ -187,7 +195,7 @@ def post(username):
# Show the username for a given id
@app.route("/user/")
def show_username(id):
- with pool.acquire() as connection:
+ with oracledb.connect(pool_alias=POOL_ALIAS_NAME) as connection:
with connection.cursor() as cursor:
cursor.execute("select username from demo where id = :idbv", [id])
r = cursor.fetchone()
@@ -198,13 +206,13 @@ def show_username(id):
if __name__ == "__main__":
# Start a pool of connections
- pool = start_pool()
+ start_pool()
# Create a demo table
create_schema()
- m = f"\nTry loading http://127.0.0.1:{port}/user/1 in a browser\n"
+ m = f"\nTry loading http://127.0.0.1:{PORT}/user/1 in a browser\n"
sys.modules["flask.cli"].show_server_banner = lambda *x: print(m)
# Start a webserver
- app.run(port=port)
+ app.run(port=PORT)
diff --git a/python/python-oracledb/sample_container/Dockerfile b/python/python-oracledb/containers/Dockerfile
similarity index 95%
rename from python/python-oracledb/sample_container/Dockerfile
rename to python/python-oracledb/containers/Dockerfile
index abae563c..0d612b8f 100644
--- a/python/python-oracledb/sample_container/Dockerfile
+++ b/python/python-oracledb/containers/Dockerfile
@@ -85,8 +85,8 @@ WORKDIR /samples/
RUN curl -LO https://github.com/oracle/python-oracledb/archive/refs/heads/main.zip && \
unzip main.zip && \
- cp python-oracledb-main/samples/sample_container/setup.py . && \
- /bin/rm -rf python-oracledb-main/samples/sample_container/ python-oracledb-main/samples/notebooks/ && \
+ cp python-oracledb-main/samples/containers/samples_and_db/setup.py . && \
+ /bin/rm -rf python-oracledb-main/samples/containers/ python-oracledb-main/samples/notebooks/ && \
mv python-oracledb-main/samples/* . && \
/bin/rm -rf python-oracledb-main samples main.zip && \
cat create_schema.py >> /samples/setup.py && \
diff --git a/python/python-oracledb/sample_container/README.md b/python/python-oracledb/containers/README.md
similarity index 100%
rename from python/python-oracledb/sample_container/README.md
rename to python/python-oracledb/containers/README.md
diff --git a/python/python-oracledb/sample_container/setup.py b/python/python-oracledb/containers/setup.py
similarity index 100%
rename from python/python-oracledb/sample_container/setup.py
rename to python/python-oracledb/containers/setup.py
diff --git a/python/python-oracledb/cqn.py b/python/python-oracledb/cqn.py
index c5c6516a..d6399f75 100644
--- a/python/python-oracledb/cqn.py
+++ b/python/python-oracledb/cqn.py
@@ -55,7 +55,7 @@ def callback(message):
registered = False
return
print("Message database name:", message.dbname)
- print("Message tranasction id:", message.txid)
+ print("Message transaction id:", message.txid)
print("Message queries:")
for query in message.queries:
print("--> Query ID:", query.id)
diff --git a/python/python-oracledb/database_change_notification.py b/python/python-oracledb/database_change_notification.py
index 7861ab2c..385e73bf 100644
--- a/python/python-oracledb/database_change_notification.py
+++ b/python/python-oracledb/database_change_notification.py
@@ -55,7 +55,7 @@ def callback(message):
registered = False
return
print("Message database name:", message.dbname)
- print("Message tranasction id:", message.txid)
+ print("Message transaction id:", message.txid)
print("Message tables:")
for table in message.tables:
print("--> Table Name:", table.name)
diff --git a/python/python-oracledb/dataframe_insert.py b/python/python-oracledb/dataframe_insert.py
new file mode 100644
index 00000000..0a1e89d6
--- /dev/null
+++ b/python/python-oracledb/dataframe_insert.py
@@ -0,0 +1,107 @@
+# -----------------------------------------------------------------------------
+# Copyright (c) 2025, Oracle and/or its affiliates.
+#
+# This software is dual-licensed to you under the Universal Permissive License
+# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
+# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
+# either license.
+#
+# If you elect to accept the software under the Apache License, Version 2.0,
+# the following applies:
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# dataframe_insert.py
+#
+# Shows how executemany() can be used to insert a Pandas dataframe directly
+# into Oracle Database. The same technique can be used with data frames from
+# many other libraries.
+# -----------------------------------------------------------------------------
+
+import sys
+import pandas
+
+import oracledb
+import sample_env
+
+# determine whether to use python-oracledb thin mode or thick mode
+if not sample_env.get_is_thin():
+ oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
+
+
+connection = oracledb.connect(
+ user=sample_env.get_main_user(),
+ password=sample_env.get_main_password(),
+ dsn=sample_env.get_connect_string(),
+ params=sample_env.get_connect_params(),
+)
+
+# -----------------------------------------------------------------------------
+#
+# Inserting a simple DataFrame
+
+with connection.cursor() as cursor:
+
+ # Create a Pandas DataFrame
+ print("Pandas Dataframe 1:")
+ d = {"A": [101, 213, 394], "B": ["Christie", "Cindy", "Kate"]}
+ pdf = pandas.DataFrame(data=d)
+ print(pdf)
+
+ # Insert data into NUMBER and VARCHAR2(20) columns using Oracle Database's
+ # efficient "Array DML" method
+ cursor.executemany("insert into mytab (id, data) values (:1, :2)", pdf)
+
+ # Check data
+ print("\nOracle Database Query:")
+ cursor.execute("select * from mytab order by id")
+ columns = [col.name for col in cursor.description]
+ print(columns)
+ for r in cursor:
+ print(r)
+
+# -----------------------------------------------------------------------------
+#
+# Inserting VECTORs
+
+# The VECTOR example only works with Oracle Database 23.4 or later
+if sample_env.get_server_version() < (23, 4):
+ sys.exit("This example requires Oracle Database 23.4 or later.")
+
+# The VECTOR example works with thin mode, or with thick mode using Oracle
+# Client 23.4 or later
+if not connection.thin and oracledb.clientversion()[:2] < (23, 4):
+ sys.exit(
+ "This example requires python-oracledb thin mode, or Oracle Client"
+ " 23.4 or later"
+ )
+
+with connection.cursor() as cursor:
+
+ # Create a Pandas DataFrame
+ print("\nPandas Dataframe 2:")
+ d = {"v": [[3.3, 1.32, 5.0], [2.2, 2.32, 2.0]]}
+ pdf = pandas.DataFrame(data=d)
+ print(pdf)
+
+ # Insert data into a VECTOR column using Oracle Database's
+ # efficient "Array DML" method
+ cursor.executemany("insert into SampleVectorTab (v64) values (:1)", pdf)
+
+ # Check data
+ print("\nOracle Database Query:")
+ cursor.execute("select v64 from SampleVectorTab order by id")
+ for (r,) in cursor:
+ print(r)
diff --git a/python/python-oracledb/dataframe_numpy.py b/python/python-oracledb/dataframe_numpy.py
index 8bc7a476..ad5f9bad 100644
--- a/python/python-oracledb/dataframe_numpy.py
+++ b/python/python-oracledb/dataframe_numpy.py
@@ -25,12 +25,14 @@
# -----------------------------------------------------------------------------
# dataframe_numpy.py
#
-# Shows how to use connection.fetch_df_all() to efficiently put data into a
-# NumPy ndarray via the DLPack standard memory layout.
+# Shows how to use connection.fetch_df_all() to put data into a NumPy ndarray
# -----------------------------------------------------------------------------
-import pyarrow
+import array
+import sys
+
import numpy
+import pyarrow
import oracledb
import sample_env
@@ -46,11 +48,14 @@
params=sample_env.get_connect_params(),
)
-SQL = "select id from SampleQueryTab order by id"
+# -----------------------------------------------------------------------------
+#
+# Fetching all records
-# Get an OracleDataFrame
+# Get a python-oracledb DataFrame
# Adjust arraysize to tune the query fetch performance
-odf = connection.fetch_df_all(statement=SQL, arraysize=100)
+sql = "select id from SampleQueryTab order by id"
+odf = connection.fetch_df_all(statement=sql, arraysize=100)
# Convert to an ndarray via the Python DLPack specification
pyarrow_array = pyarrow.array(odf.get_column_by_name("ID"))
@@ -62,10 +67,56 @@
print("Type:")
print(type(np)) #
-# Perform various numpy operations on the ndarray
+print("Values:")
+print(np)
+
+# Perform various NumPy operations on the ndarray
print("\nSum:")
print(numpy.sum(np))
print("\nLog10:")
print(numpy.log10(np))
+
+# -----------------------------------------------------------------------------
+#
+# Fetching VECTORs
+
+# The VECTOR example only works with Oracle Database 23.4 or later
+if sample_env.get_server_version() < (23, 4):
+ sys.exit("This example requires Oracle Database 23.4 or later.")
+
+# The VECTOR example works with thin mode, or with thick mode using Oracle
+# Client 23.4 or later
+if not connection.thin and oracledb.clientversion()[:2] < (23, 4):
+ sys.exit(
+ "This example requires python-oracledb thin mode, or Oracle Client"
+ " 23.4 or later"
+ )
+
+# Insert sample data
+rows = [
+ (array.array("d", [11.25, 11.75, 11.5]),),
+ (array.array("d", [12.25, 12.75, 12.5]),),
+]
+
+with connection.cursor() as cursor:
+ cursor.executemany("insert into SampleVectorTab (v64) values (:1)", rows)
+
+# Get a python-oracledb DataFrame
+# Adjust arraysize to tune the query fetch performance
+sql = "select v64 from SampleVectorTab order by id"
+odf = connection.fetch_df_all(statement=sql, arraysize=100)
+
+# Convert to a NumPy ndarray
+pyarrow_array = pyarrow.array(odf.get_column_by_name("V64"))
+np = pyarrow_array.to_numpy(zero_copy_only=False)
+
+print("Type:")
+print(type(np)) #
+
+print("Values:")
+print(np)
+
+print("\nSum:")
+print(numpy.sum(np))
diff --git a/python/python-oracledb/dataframe_pandas.py b/python/python-oracledb/dataframe_pandas.py
index f233703f..49c13be0 100644
--- a/python/python-oracledb/dataframe_pandas.py
+++ b/python/python-oracledb/dataframe_pandas.py
@@ -29,6 +29,10 @@
# to create Pandas dataframes.
# -----------------------------------------------------------------------------
+import array
+import sys
+
+import numpy
import pandas
import pyarrow
@@ -46,20 +50,17 @@
params=sample_env.get_connect_params(),
)
-SQL = "select id, name from SampleQueryTab order by id"
-
# -----------------------------------------------------------------------------
#
# Fetching all records
-# Get an OracleDataFrame.
+# Get a python-oracledb DataFrame.
# Adjust arraysize to tune the query fetch performance
-odf = connection.fetch_df_all(statement=SQL, arraysize=100)
+sql = "select id, name from SampleQueryTab order by id"
+odf = connection.fetch_df_all(statement=sql, arraysize=100)
# Get a Pandas DataFrame from the data
-df = pyarrow.Table.from_arrays(
- odf.column_arrays(), names=odf.column_names()
-).to_pandas()
+df = pyarrow.table(odf).to_pandas()
# Perform various Pandas operations on the DataFrame
@@ -88,10 +89,9 @@
# Tune 'size' for your data set. Here it is small to show the batch fetch
# behavior on the sample table.
-for odf in connection.fetch_df_batches(statement=SQL, size=10):
- df_b = pyarrow.Table.from_arrays(
- odf.column_arrays(), names=odf.column_names()
- ).to_pandas()
+sql = "select id, name from SampleQueryTab order by id"
+for odf in connection.fetch_df_batches(statement=sql, size=10):
+ df_b = pyarrow.table(odf).to_pandas()
print(f"Appending {df_b.shape[0]} rows")
df = pandas.concat([df, df_b], ignore_index=True)
@@ -100,3 +100,78 @@
print("\nLast three rows:")
print(df.tail(3))
+
+# -----------------------------------------------------------------------------
+#
+# Fetching VECTORs
+
+# The VECTOR example only works with Oracle Database 23.4 or later
+if sample_env.get_server_version() < (23, 4):
+ sys.exit("This example requires Oracle Database 23.4 or later.")
+
+# The VECTOR example works with thin mode, or with thick mode using Oracle
+# Client 23.4 or later
+if not connection.thin and oracledb.clientversion()[:2] < (23, 4):
+ sys.exit(
+ "This example requires python-oracledb thin mode, or Oracle Client"
+ " 23.4 or later"
+ )
+
+# Insert sample data
+rows = [
+ (array.array("d", [11.25, 11.75, 11.5]),),
+ (array.array("d", [12.25, 12.75, 12.5]),),
+]
+
+with connection.cursor() as cursor:
+ cursor.executemany("insert into SampleVectorTab (v64) values (:1)", rows)
+
+
+# Get a python-oracledb DataFrame.
+# Adjust arraysize to tune the query fetch performance
+sql = "select id, v64 from SampleVectorTab order by id"
+odf = connection.fetch_df_all(statement=sql, arraysize=100)
+
+# Get a Pandas DataFrame from the data
+df = pyarrow.table(odf).to_pandas()
+
+# Perform various Pandas operations on the DataFrame
+
+print("\nDataFrame:")
+print(df)
+
+print("\nMean:")
+print(pandas.DataFrame(df["V64"].tolist()).mean())
+
+print("\nList:")
+df2 = pandas.DataFrame(df["V64"].tolist()).T
+print(df2)
+print(df2.sum())
+
+# You can manipulate vectors using Pandas's apply or list comprehension with
+# NumPy for efficient array operations.
+
+# Scaling all vectors by a factor of two
+print("\nScaled:")
+df["SCALED_V64_COL"] = df["V64"] * 2
+print(df)
+
+# Calculating vector norms
+#
+# L2_NORM = Straight line distance from the origin to vector's endpoint
+# L1_NORM = Sum of absolute values of the vector's components
+# Linf_NORM = Largest absolute component of the vector; useful in scenarios
+# where maximum deviation matters
+print("\nNorms:")
+df["L2_NORM"] = df["V64"].apply(lambda x: numpy.linalg.norm(x, ord=2))
+df["L1_NORM"] = df["V64"].apply(lambda x: numpy.linalg.norm(x, ord=1))
+df["Linf_NORM"] = df["V64"].apply(
+ lambda x: numpy.linalg.norm(x, ord=numpy.inf)
+)
+print(df)
+
+# Calculating the vector dot product with a reference vector
+print("\nDot product:")
+ref_vector = numpy.array([1, 10, 10])
+df["DOT_PRODUCT"] = df["V64"].apply(lambda x: numpy.dot(x, ref_vector))
+print(df)
diff --git a/python/python-oracledb/dataframe_pandas_async.py b/python/python-oracledb/dataframe_pandas_async.py
index 796da794..eaadead2 100644
--- a/python/python-oracledb/dataframe_pandas_async.py
+++ b/python/python-oracledb/dataframe_pandas_async.py
@@ -33,8 +33,11 @@
# other, synchronous, data frame samples.
# -----------------------------------------------------------------------------
+import array
import asyncio
+import sys
+import numpy
import pandas
import pyarrow
@@ -56,14 +59,12 @@ async def main():
#
# Fetching all records
- # Get an OracleDataFrame.
+ # Get a python-oracledb DataFrame.
# Adjust arraysize to tune the query fetch performance
odf = await connection.fetch_df_all(statement=SQL, arraysize=100)
# Get a Pandas DataFrame from the data
- df = pyarrow.Table.from_arrays(
- odf.column_arrays(), names=odf.column_names()
- ).to_pandas()
+ df = pyarrow.table(odf).to_pandas()
# Perform various Pandas operations on the DataFrame
@@ -93,9 +94,7 @@ async def main():
# Tune 'size' for your data set. Here it is small to show the batch fetch
# behavior on the sample table.
async for odf in connection.fetch_df_batches(statement=SQL, size=10):
- df_b = pyarrow.Table.from_arrays(
- odf.column_arrays(), names=odf.column_names()
- ).to_pandas()
+ df_b = pyarrow.table(odf).to_pandas()
print(f"Appending {df_b.shape[0]} rows")
df = pandas.concat([df, df_b], ignore_index=True)
@@ -105,5 +104,81 @@ async def main():
print("\nLast three rows:")
print(df.tail(3))
+ # -------------------------------------------------------------------------
+ #
+ # Fetching VECTORs
+
+ # The VECTOR example only works with Oracle Database 23.4 or later
+ if sample_env.get_server_version() < (23, 4):
+ sys.exit("This example requires Oracle Database 23.4 or later.")
+
+ # The VECTOR example works with thin mode, or with thick mode using Oracle
+ # Client 23.4 or later
+ if not connection.thin and oracledb.clientversion()[:2] < (23, 4):
+ sys.exit(
+ "This example requires python-oracledb thin mode, or Oracle Client"
+ " 23.4 or later"
+ )
+
+ # Insert sample data
+ rows = [
+ (array.array("d", [11.25, 11.75, 11.5]),),
+ (array.array("d", [12.25, 12.75, 12.5]),),
+ ]
+
+ with connection.cursor() as cursor:
+ await cursor.executemany(
+ "insert into SampleVectorTab (v64) values (:1)", rows
+ )
+
+ # Get a python-oracledb DataFrame.
+ # Adjust arraysize to tune the query fetch performance
+ sql = "select id, v64 from SampleVectorTab order by id"
+ odf = await connection.fetch_df_all(statement=sql, arraysize=100)
+
+ # Get a Pandas DataFrame from the data
+ df = pyarrow.table(odf).to_pandas()
+
+ # Perform various Pandas operations on the DataFrame
+
+ print("\nDataFrame:")
+ print(df)
+
+ print("\nMean:")
+ print(pandas.DataFrame(df["V64"].tolist()).mean())
+
+ print("\nList:")
+ df2 = pandas.DataFrame(df["V64"].tolist()).T
+ print(df2)
+ print(df2.sum())
+
+ # You can manipulate vectors using Pandas's apply or list comprehension
+ # with NumPy for efficient array operations.
+
+ # Scaling all vectors by a factor of two
+ print("\nScaled:")
+ df["SCALED_V64_COL"] = df["V64"].apply(lambda x: numpy.array(x) * 2)
+ print(df)
+
+ # Calculating vector norms
+ #
+ # L2_NORM = Straight line distance from the origin to vector's endpoint
+ # L1_NORM = Sum of absolute values of the vector's components
+ # Linf_NORM = Largest absolute component of the vector; useful in scenarios
+ # where maximum deviation matters
+ print("\nNorms:")
+ df["L2_NORM"] = df["V64"].apply(lambda x: numpy.linalg.norm(x, ord=2))
+ df["L1_NORM"] = df["V64"].apply(lambda x: numpy.linalg.norm(x, ord=1))
+ df["Linf_NORM"] = df["V64"].apply(
+ lambda x: numpy.linalg.norm(x, ord=numpy.inf)
+ )
+ print(df)
+
+ # Calculating the vector dot product with a reference vector
+ print("\nDot product:")
+ ref_vector = numpy.array([1, 10, 10])
+ df["DOT_PRODUCT"] = df["V64"].apply(lambda x: numpy.dot(x, ref_vector))
+ print(df)
+
asyncio.run(main())
diff --git a/python/python-oracledb/dataframe_parquet_write.py b/python/python-oracledb/dataframe_parquet_write.py
index 02a7d93f..7a023859 100644
--- a/python/python-oracledb/dataframe_parquet_write.py
+++ b/python/python-oracledb/dataframe_parquet_write.py
@@ -61,9 +61,7 @@
for odf in connection.fetch_df_batches(statement=SQL, size=FETCH_BATCH_SIZE):
- pyarrow_table = pyarrow.Table.from_arrays(
- arrays=odf.column_arrays(), names=odf.column_names()
- )
+ pyarrow_table = pyarrow.table(odf)
if not pqwriter:
pqwriter = pq.ParquetWriter(PARQUET_FILE_NAME, pyarrow_table.schema)
diff --git a/python/python-oracledb/dataframe_polars.py b/python/python-oracledb/dataframe_polars.py
index 7b91ced7..5d1aed22 100644
--- a/python/python-oracledb/dataframe_polars.py
+++ b/python/python-oracledb/dataframe_polars.py
@@ -52,15 +52,12 @@
SQL1 = "select * from SampleQueryTab order by id"
-# Get an OracleDataFrame
+# Get a python-oracledb DataFrame
# Adjust arraysize to tune the query fetch performance
odf = connection.fetch_df_all(statement=SQL1, arraysize=100)
# Convert to a Polars DataFrame
-pyarrow_table = pyarrow.Table.from_arrays(
- odf.column_arrays(), names=odf.column_names()
-)
-p = polars.from_arrow(pyarrow_table)
+p = polars.from_arrow(odf)
print(type(p)) #
@@ -76,7 +73,7 @@
SQL2 = "select id from SampleQueryTab order by id"
-# Get an OracleDataFrame
+# Get a python-oracledb DataFrame
# Adjust arraysize to tune the query fetch performance
odf = connection.fetch_df_all(statement=SQL2, arraysize=100)
diff --git a/python/python-oracledb/dataframe_pyarrow.py b/python/python-oracledb/dataframe_pyarrow.py
index d666f62b..1cc56dab 100644
--- a/python/python-oracledb/dataframe_pyarrow.py
+++ b/python/python-oracledb/dataframe_pyarrow.py
@@ -51,14 +51,12 @@
SQL1 = "select id, name from SampleQueryTab order by id"
-# Get an OracleDataFrame
+# Get a python-oracledb DataFrame
# Adjust arraysize to tune the query fetch performance
odf = connection.fetch_df_all(statement=SQL1, arraysize=100)
# Create a PyArrow table
-pyarrow_table = pyarrow.Table.from_arrays(
- arrays=odf.column_arrays(), names=odf.column_names()
-)
+pyarrow_table = pyarrow.table(odf)
print("Type:")
print(type(pyarrow_table)) #
@@ -78,7 +76,7 @@
SQL2 = "select id from SampleQueryTab order by id"
-# Get an OracleDataFrame
+# Get a python-oracledb DataFrame
# Adjust arraysize to tune the query fetch performance
odf = connection.fetch_df_all(statement=SQL2, arraysize=100)
diff --git a/python/python-oracledb/dataframe_torch.py b/python/python-oracledb/dataframe_torch.py
index e45d1940..de2d0113 100644
--- a/python/python-oracledb/dataframe_torch.py
+++ b/python/python-oracledb/dataframe_torch.py
@@ -48,7 +48,7 @@
SQL = "select id from SampleQueryTab order by id"
-# Get an OracleDataFrame
+# Get a python-oracledb DataFrame
# Adjust arraysize to tune the query fetch performance
odf = connection.fetch_df_all(statement=SQL, arraysize=100)
diff --git a/python/python-oracledb/json_blob.py b/python/python-oracledb/json_blob.py
index 61b8576f..e0d06658 100644
--- a/python/python-oracledb/json_blob.py
+++ b/python/python-oracledb/json_blob.py
@@ -57,7 +57,7 @@
client_version = oracledb.clientversion()[0]
db_version = int(connection.version.split(".")[0])
-# Minimum database vesion is 12
+# Minimum database version is 12
if db_version < 12:
sys.exit("This example requires Oracle Database 12.1.0.2 or later")
diff --git a/python/python-oracledb/json_blob_async.py b/python/python-oracledb/json_blob_async.py
index d8b9221d..5c9fb45c 100644
--- a/python/python-oracledb/json_blob_async.py
+++ b/python/python-oracledb/json_blob_async.py
@@ -54,7 +54,7 @@ async def main():
params=sample_env.get_connect_params(),
)
- # Minimum database vesion is 12
+ # Minimum database version is 12
db_version = int(connection.version.split(".")[0])
if db_version < 12:
sys.exit("This example requires Oracle Database 12.1.0.2 or later")
diff --git a/python/python-oracledb/multi_consumer_aq_async.py b/python/python-oracledb/multi_consumer_aq_async.py
new file mode 100644
index 00000000..12c405e9
--- /dev/null
+++ b/python/python-oracledb/multi_consumer_aq_async.py
@@ -0,0 +1,96 @@
+# -----------------------------------------------------------------------------
+# Copyright (c) 2025, Oracle and/or its affiliates.
+#
+# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
+#
+# Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
+# Canada. All rights reserved.
+#
+# This software is dual-licensed to you under the Universal Permissive License
+# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
+# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
+# either license.
+#
+# If you elect to accept the software under the Apache License, Version 2.0,
+# the following applies:
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# multi_consumer_aq.py
+#
+# Demonstrates how to use multi-consumer advanced queuing. It makes use of a
+# RAW queue created in the sample setup.
+# -----------------------------------------------------------------------------
+
+import asyncio
+
+import oracledb
+import sample_env
+
+QUEUE_NAME = "DEMO_RAW_QUEUE_MULTI"
+PAYLOAD_DATA = [
+ "The first message",
+ "The second message",
+ "The third message",
+ "The fourth and final message",
+]
+
+
+async def main():
+
+ # connect to database
+ connection = await oracledb.connect_async(
+ user=sample_env.get_main_user(),
+ password=sample_env.get_main_password(),
+ dsn=sample_env.get_connect_string(),
+ )
+
+ # create a queue
+ queue = connection.queue(QUEUE_NAME)
+ queue.deqoptions.wait = oracledb.DEQ_NO_WAIT
+ queue.deqoptions.navigation = oracledb.DEQ_FIRST_MSG
+
+ # enqueue a few messages
+ print("Enqueuing messages...")
+ for data in PAYLOAD_DATA:
+ print(data)
+ await queue.enqone(connection.msgproperties(payload=data))
+ await connection.commit()
+ print()
+
+ # dequeue the messages for consumer A
+ print("Dequeuing the messages for consumer A...")
+ queue.deqoptions.consumername = "SUBSCRIBER_A"
+ while True:
+ props = await queue.deqone()
+ if not props:
+ break
+ print(props.payload.decode())
+ await connection.commit()
+ print()
+
+ # dequeue the message for consumer B
+ print("Dequeuing the messages for consumer B...")
+ queue.deqoptions.consumername = "SUBSCRIBER_B"
+ while True:
+ props = await queue.deqone()
+ if not props:
+ break
+ print(props.payload.decode())
+ await connection.commit()
+ print("\nDone.")
+
+
+asyncio.run(main())
diff --git a/python/python-oracledb/object_aq_async.py b/python/python-oracledb/object_aq_async.py
new file mode 100644
index 00000000..1dda7de0
--- /dev/null
+++ b/python/python-oracledb/object_aq_async.py
@@ -0,0 +1,101 @@
+# -----------------------------------------------------------------------------
+# Copyright (c) 2025, Oracle and/or its affiliates.
+#
+# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
+#
+# Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
+# Canada. All rights reserved.
+#
+# This software is dual-licensed to you under the Universal Permissive License
+# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
+# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
+# either license.
+#
+# If you elect to accept the software under the Apache License, Version 2.0,
+# the following applies:
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# object_aq.py
+#
+# Demonstrates how to use advanced queuing with objects. It makes use of a
+# simple type and queue created in the sample setup.
+# -----------------------------------------------------------------------------
+
+import asyncio
+import decimal
+
+import oracledb
+import sample_env
+
+BOOK_TYPE_NAME = "UDT_BOOK"
+QUEUE_NAME = "DEMO_BOOK_QUEUE"
+BOOK_DATA = [
+ (
+ "The Fellowship of the Ring",
+ "Tolkien, J.R.R.",
+ decimal.Decimal("10.99"),
+ ),
+ (
+ "Harry Potter and the Philosopher's Stone",
+ "Rowling, J.K.",
+ decimal.Decimal("7.99"),
+ ),
+]
+
+
+async def main():
+
+ # connect to database
+ connection = await oracledb.connect_async(
+ user=sample_env.get_main_user(),
+ password=sample_env.get_main_password(),
+ dsn=sample_env.get_connect_string(),
+ )
+
+ # create a queue
+ books_type = await connection.gettype(BOOK_TYPE_NAME)
+ queue = connection.queue(QUEUE_NAME, payload_type=books_type)
+ queue.deqoptions.wait = oracledb.DEQ_NO_WAIT
+ queue.deqoptions.navigation = oracledb.DEQ_FIRST_MSG
+
+ # dequeue all existing messages to ensure the queue is empty, just so that
+ # the results are consistent
+ while await queue.deqone():
+ pass
+
+ # enqueue a few messages
+ print("Enqueuing messages...")
+ for title, authors, price in BOOK_DATA:
+ book = books_type.newobject()
+ book.TITLE = title
+ book.AUTHORS = authors
+ book.PRICE = price
+ print(title)
+ await queue.enqone(connection.msgproperties(payload=book))
+ await connection.commit()
+
+ # dequeue the messages
+ print("\nDequeuing messages...")
+ while True:
+ props = await queue.deqone()
+ if not props:
+ break
+ print(props.payload.TITLE)
+ await connection.commit()
+ print("\nDone.")
+
+
+asyncio.run(main())
diff --git a/python/python-oracledb/raw_aq_async.py b/python/python-oracledb/raw_aq_async.py
new file mode 100644
index 00000000..8d25b614
--- /dev/null
+++ b/python/python-oracledb/raw_aq_async.py
@@ -0,0 +1,86 @@
+# -----------------------------------------------------------------------------
+# Copyright (c) 2025, Oracle and/or its affiliates.
+#
+# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
+#
+# Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
+# Canada. All rights reserved.
+#
+# This software is dual-licensed to you under the Universal Permissive License
+# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
+# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
+# either license.
+#
+# If you elect to accept the software under the Apache License, Version 2.0,
+# the following applies:
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# raw_aq.py
+#
+# Demonstrates how to use advanced queuing with RAW data. It makes use of a
+# RAW queue created in the sample setup.
+# -----------------------------------------------------------------------------
+
+import asyncio
+
+import oracledb
+import sample_env
+
+QUEUE_NAME = "DEMO_RAW_QUEUE"
+PAYLOAD_DATA = [
+ "The first message",
+ "The second message",
+ "The third message",
+ "The fourth and final message",
+]
+
+
+async def main():
+ connection = await oracledb.connect_async(
+ user=sample_env.get_main_user(),
+ password=sample_env.get_main_password(),
+ dsn=sample_env.get_connect_string(),
+ )
+
+ # create a queue
+ queue = connection.queue(QUEUE_NAME)
+ queue.deqoptions.wait = oracledb.DEQ_NO_WAIT
+ queue.deqoptions.navigation = oracledb.DEQ_FIRST_MSG
+
+ # dequeue all existing messages to ensure the queue is empty, just so
+ # that the results are consistent
+ while await queue.deqone():
+ pass
+
+ # enqueue a few messages
+ print("Enqueuing messages...")
+ for data in PAYLOAD_DATA:
+ print(data)
+ await queue.enqone(connection.msgproperties(payload=data))
+ await connection.commit()
+
+ # dequeue the messages
+ print("\nDequeuing messages...")
+ while True:
+ props = await queue.deqone()
+ if not props:
+ break
+ print(props.payload.decode())
+ await connection.commit()
+ print("\nDone.")
+
+
+asyncio.run(main())
diff --git a/python/python-oracledb/scrollable_cursors.py b/python/python-oracledb/scrollable_cursors.py
index 90699842..a1341dd3 100644
--- a/python/python-oracledb/scrollable_cursors.py
+++ b/python/python-oracledb/scrollable_cursors.py
@@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
-# Copyright (c) 2016, 2023, Oracle and/or its affiliates.
+# Copyright (c) 2016, 2025, Oracle and/or its affiliates.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@@ -38,8 +38,9 @@
import oracledb
import sample_env
-# this script is currently only supported in python-oracledb thick mode
-oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
+# determine whether to use python-oracledb thin mode or thick mode
+if not sample_env.get_is_thin():
+ oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
connection = oracledb.connect(
user=sample_env.get_main_user(),
diff --git a/python/python-oracledb/sessionless_transactions.py b/python/python-oracledb/sessionless_transactions.py
new file mode 100644
index 00000000..c0b31b07
--- /dev/null
+++ b/python/python-oracledb/sessionless_transactions.py
@@ -0,0 +1,157 @@
+# -----------------------------------------------------------------------------
+# Copyright (c) 2025 Oracle and/or its affiliates.
+#
+# This software is dual-licensed to you under the Universal Permissive License
+# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
+# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
+# either license.
+#
+# If you elect to accept the software under the Apache License, Version 2.0,
+# the following applies:
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# sessionless_transactions.py
+#
+# Show Oracle Database 23ai Sessionless Transactions
+# -----------------------------------------------------------------------------
+
+import sys
+
+import oracledb
+import sample_env
+
+# determine whether to use python-oracledb thin mode or thick mode
+if not sample_env.get_is_thin():
+ oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
+
+# this script only works with Oracle Database 23.6 or later
+if sample_env.get_server_version() < (23, 6):
+ sys.exit("This example requires Oracle Database 23.6 or later.")
+
+# this script works with thin mode, or with thick mode using Oracle Client
+# 23.6 or later
+if not oracledb.is_thin_mode() and oracledb.clientversion()[:2] < (23, 6):
+ sys.exit(
+ "This example requires python-oracledb thin mode, or Oracle Client"
+ " 23.6 or later"
+ )
+
+TXN_ID = b"my_transaction_id"
+
+pool = oracledb.create_pool(
+ user=sample_env.get_main_user(),
+ password=sample_env.get_main_password(),
+ dsn=sample_env.get_connect_string(),
+ params=sample_env.get_pool_params(),
+)
+
+# -----------------------------------------------------------------------------
+# Basic Sessionless Transaction example
+
+print("Example 1:")
+
+# Start and suspend a transaction
+with pool.acquire() as connection1:
+
+ # Immediately begin the transaction
+ connection1.begin_sessionless_transaction(transaction_id=TXN_ID)
+
+ with connection1.cursor() as cursor1:
+ cursor1.execute(
+ "insert into mytab(id, data) values (:i, :d)", [1, "Sessionless 1"]
+ )
+ connection1.suspend_sessionless_transaction()
+
+ # Since the transaction is suspended, there will be no rows
+ print("1st query")
+ with connection1.cursor() as cursor1b:
+ for r in cursor1b.execute("select * from mytab"):
+ print(r)
+
+# Resume and complete the transaction in a different connection
+with pool.acquire() as connection2:
+
+ # Immediately resume the transaction
+ connection2.resume_sessionless_transaction(transaction_id=TXN_ID)
+
+ with connection2.cursor() as cursor2:
+ cursor2.execute(
+ "insert into mytab(id, data) values (:i, :d)", [2, "Sessionless 2"]
+ )
+
+ # The query will show both rows inserted
+ print("2nd query")
+ for r in cursor2.execute("select * from mytab order by id"):
+ print(r)
+
+ # Rollback so the example can be run multiple times.
+ # This concludes the Sessionless Transaction
+ connection2.rollback()
+
+# -----------------------------------------------------------------------------
+# Sessionless Transaction example with custom timeouts and round-trip
+# optimizations
+
+print("Example 2:")
+
+# Start and suspend a transaction
+with pool.acquire() as connection3:
+
+ connection3.begin_sessionless_transaction(
+ transaction_id=TXN_ID,
+ # The transaction can only ever be suspended for 15 seconds before it
+ # is automatically rolled back
+ timeout=15,
+ # Only start the transaction when the next DB operation is performed
+ defer_round_trip=True,
+ )
+ with connection3.cursor() as cursor3:
+ cursor3.execute(
+ "insert into mytab(id, data) values (:i, :d)",
+ [3, "Sessionless 3"],
+ suspend_on_success=True, # automatically suspend on success
+ )
+
+ # Since the transaction is suspended, there will be no rows
+ print("1st query")
+ with connection3.cursor() as cursor3b:
+ for r in cursor3b.execute("select * from mytab"):
+ print(r)
+
+# Resume and complete the transaction in a different connection
+with pool.acquire() as connection4:
+ connection4.resume_sessionless_transaction(
+ transaction_id=TXN_ID,
+ # Only wait 20 seconds if someone else is using the transaction
+ timeout=20,
+ # Only initiate resuming the transaction when the next DB operation is
+ # performed
+ defer_round_trip=True,
+ )
+
+ with connection4.cursor() as cursor4:
+ cursor4.execute(
+ "insert into mytab(id, data) values (:i, :d)", [4, "Sessionless 4"]
+ )
+
+ # The query will show both rows inserted
+ print("2nd query")
+ for r in cursor4.execute("select * from mytab order by id"):
+ print(r)
+
+ # Rollback so the example can be run multiple times.
+ # This concludes the Sessionless Transaction
+ connection4.rollback()
diff --git a/python/python-oracledb/sql/create_schema_23.sql b/python/python-oracledb/sql/create_schema_23.sql
index 30daef5f..53226f02 100644
--- a/python/python-oracledb/sql/create_schema_23.sql
+++ b/python/python-oracledb/sql/create_schema_23.sql
@@ -32,6 +32,7 @@
*---------------------------------------------------------------------------*/
create table &main_user..SampleVectorTab (
+ id number generated by default on null as identity primary key,
v32 vector(3, float32),
v64 vector(3, float64),
v8 vector(3, int8),
diff --git a/python/python-oracledb/sql/drop_schema.sql b/python/python-oracledb/sql/drop_schema.sql
index 82c6c949..62e11959 100644
--- a/python/python-oracledb/sql/drop_schema.sql
+++ b/python/python-oracledb/sql/drop_schema.sql
@@ -1,5 +1,5 @@
/*-----------------------------------------------------------------------------
- * Copyright 2017, 2022, Oracle and/or its affiliates.
+ * Copyright 2017, 2025, Oracle and/or its affiliates.
*
* This software is dual-licensed to you under the Universal Permissive License
* (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@@ -43,7 +43,9 @@ begin
for r in
( select edition_name
from dba_editions
- where edition_name in (upper('&edition_name'))
+ start with edition_name = upper('&edition_name')
+ connect by prior edition_name = parent_edition_name
+ order by level desc
) loop
execute immediate 'drop edition ' || r.edition_name || ' cascade';
end loop;
diff --git a/python/python-oracledb/transaction_guard.py b/python/python-oracledb/transaction_guard.py
index a9f8fdf5..5a9c286a 100644
--- a/python/python-oracledb/transaction_guard.py
+++ b/python/python-oracledb/transaction_guard.py
@@ -93,6 +93,9 @@
input("Press ENTER when complete.")
+ltxid = connection.ltxid
+if not ltxid:
+ sys.exit("Logical transaction not available. Terminating.")
try:
connection.commit() # this should fail
sys.exit("Session was not killed. Sample cannot continue.")
@@ -101,9 +104,6 @@
print("Session is recoverable:", error_obj.isrecoverable)
if not error_obj.isrecoverable:
sys.exit("Session is not recoverable. Terminating.")
-ltxid = connection.ltxid
-if not ltxid:
- sys.exit("Logical transaction not available. Terminating.")
pool.drop(connection)
# check if previous transaction completed
diff --git a/sagas/CloudBank-DEMO/pom.xml b/sagas/CloudBank-DEMO/pom.xml
index bcd775f3..1f92dabe 100644
--- a/sagas/CloudBank-DEMO/pom.xml
+++ b/sagas/CloudBank-DEMO/pom.xml
@@ -65,7 +65,7 @@
ch.qos.logback
logback-core
- 1.5.13
+ 1.5.19
ch.qos.logback
diff --git a/security/data-redaction/README.md b/security/data-redaction/README.md
new file mode 100644
index 00000000..ea5bf169
--- /dev/null
+++ b/security/data-redaction/README.md
@@ -0,0 +1,55 @@
+# Demo: Exploring Data Redaction Enhancements in Oracle Database 23ai
+
+This demo script showcases new **Oracle Data Redaction** capabilities in Oracle Database 23ai (23.6+).
+It walks through creating and altering redaction policies, applying them to tables, virtual columns, indexes, and views, and observing how results differ for **HR** versus non-HR users.
+
+## Requirements
+
+- **Oracle Database 23ai 23.6+**
+- **HR sample schema** installed from [Oracle Sample Schemas](https://github.com/oracle-samples/db-sample-schemas/releases)
+- Privileges to run `DBMS_REDACT` and alter objects in the HR schema (recommended: run as HR)
+
+
+## Script Sections
+
+### 0. Verify Setup
+- Confirms **HR schema** exists
+- Confirms **Database version ≥ 23**
+- Script exits if either requirement is not met
+
+### 1. Basic Redaction Policy
+- Creates a policy on `HR.EMPLOYEES`
+- Redacts data for all users except HR
+
+### 2. Mathematical and Set Operators
+- Adds redaction to the `SALARY` column
+- Demonstrates impact on `GROUP BY` queries (e.g., average salary)
+
+### 3. Redaction with GROUP BY and ORDER BY
+- Queries team statistics by manager
+- Shows difference in totals/order for HR vs non-HR users
+
+### 4. Redacting Virtual Columns & Function-Based Indexes
+- Creates a function-based index on `PHONE_NUMBER`
+- Adds a virtual column (`ROUNDED_SALARY`)
+- Applies redaction to `PHONE_NUMBER`
+- Updates default redaction values (e.g., numbers -> 0, chars -> X)
+- Queries redacted results
+
+### 5. Redaction in Views with Expressions
+- Creates a view `HR.EMPLOYEE_VIEW` with calculated columns
+- Redacts `FIRST_NAME` (full)
+- Redacts `EMAIL` using **regular expression replacement**
+- Queries the view to illustrate differences for HR vs non-HR users
+
+---
+
+## How to Run
+1. Connect as a user with privileges (e.g., `HR`) in **SQL*Plus** or **SQL Developer**
+2. Execute the script
+
+
+## To learn more about Oracle Data Redaction:
+- Visit the Advanced Security product page (https://www.oracle.com/security/database-security/advanced-security/#redact-data) on the Oracle website.
+- For hands-on experience, try our free, interactive Oracle Data Redaction LiveLab (https://apexapps.oracle.com/pls/apex/r/dbpm/livelabs/view-workshop?wid=4061&clear=RR,180&session=1856055320747).
+
diff --git a/security/data-redaction/demo.sql b/security/data-redaction/demo.sql
new file mode 100644
index 00000000..f47ef840
--- /dev/null
+++ b/security/data-redaction/demo.sql
@@ -0,0 +1,187 @@
+--------------------------------------------------------------------------------
+-- DEMO SCRIPT: Exploring Data Redaction Enhancements in Oracle Database 23ai
+-- Requirements:
+-- (1) Oracle Database 23ai 23.6+
+-- (2) HR sample schema installed from https://github.com/oracle-samples/db-sample-schemas/releases
+-- (3) Privileges to run DBMS_REDACT and alter HR objects (or run as HR)
+--------------------------------------------------------------------------------
+
+--------------------------------------------------------------------------------
+-- 0. Verify setup
+--------------------------------------------------------------------------------
+SET SERVEROUTPUT ON;
+
+DECLARE
+ v_count NUMBER;
+ v_version NUMBER;
+BEGIN
+ -- Check HR schema
+ SELECT COUNT(*) INTO v_count FROM all_users WHERE username = 'HR';
+ IF v_count = 0 THEN
+ RAISE_APPLICATION_ERROR(-20001, 'ERROR: HR schema not found. Exiting.');
+ END IF;
+
+ -- Check DB version >= 23
+ SELECT TO_NUMBER(REGEXP_SUBSTR(version, '^[0-9]+')) INTO v_version FROM v$instance;
+ IF v_version < 23 THEN
+ RAISE_APPLICATION_ERROR(-20002, 'ERROR: Requires DB version 23 or higher. Exiting.');
+ END IF;
+END;
+/
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+-- 1. Create a basic redaction policy
+-- Redact data for all users except HR
+--------------------------------------------------------------------------------
+BEGIN
+ DBMS_REDACT.ADD_POLICY(
+ object_schema => 'HR',
+ object_name => 'EMPLOYEES',
+ policy_name => 'REDACT_DATA',
+ expression => 'SYS_CONTEXT(''USERENV'',''SESSION_USER'') != ''HR'''
+ );
+END;
+/
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+-- 2. Mathematical and set operators
+-- Alter redaction policy to include SALARY column for full redaction
+--------------------------------------------------------------------------------
+BEGIN
+ DBMS_REDACT.ALTER_POLICY(
+ object_schema => 'HR',
+ object_name => 'EMPLOYEES',
+ policy_name => 'REDACT_DATA',
+ column_name => 'SALARY',
+ action => DBMS_REDACT.ADD_COLUMN,
+ function_type => DBMS_REDACT.FULL
+ );
+END;
+/
+--------------------------------------------------------------------------------
+
+-- Query employee statistics
+SELECT department_id AS dept_id,
+ COUNT(employee_id) AS emp_count,
+ AVG(salary) AS avg_salary
+FROM hr.employees
+GROUP BY department_id
+FETCH FIRST 5 ROWS ONLY;
+--------------------------------------------------------------------------------
+-- HR user sees real values; non-HR sees 0 for avg_salary
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+-- 3. Redaction with GROUP BY and ORDER BY
+--------------------------------------------------------------------------------
+SELECT manager_id,
+ COUNT(DISTINCT employee_id) AS direct_reports,
+ SUM(salary) AS total_team_salary
+FROM hr.employees
+GROUP BY manager_id
+ORDER BY total_team_salary DESC
+FETCH FIRST 5 ROWS ONLY;
+--------------------------------------------------------------------------------
+-- HR sees totals; non-HR sees 0 (rows may order differently)
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+-- 4. Redacting virtual columns in function-based indexes
+--------------------------------------------------------------------------------
+
+-- Step 1: Create a function-based index (removes periods and hyphens)
+CREATE INDEX hr.phone_number_idx
+ ON hr.employees(
+ REPLACE(REPLACE(phone_number, '.', ''), '-', '')
+ );
+
+-- Step 2: Add a virtual column for rounded salary
+ALTER TABLE hr.employees ADD (
+ rounded_salary AS (ROUND(salary, -3))
+);
+
+-- Step 3: Apply redaction to PHONE_NUMBER
+BEGIN
+ DBMS_REDACT.ALTER_POLICY(
+ object_schema => 'HR',
+ object_name => 'EMPLOYEES',
+ column_name => 'PHONE_NUMBER',
+ policy_name => 'REDACT_DATA',
+ action => DBMS_REDACT.ADD_COLUMN,
+ function_type => DBMS_REDACT.FULL
+ );
+END;
+/
+
+-- Step 4: Update default redaction values
+BEGIN
+ DBMS_REDACT.UPDATE_FULL_REDACTION_VALUES(
+ number_value => 0,
+ char_value => 'X'
+ );
+END;
+/
+
+-- Step 5: Query redacted virtual columns
+SELECT employee_id, phone_number, rounded_salary
+FROM hr.employees
+WHERE employee_id IN (101, 103, 176, 201);
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+-- 5. Redaction in Views with Expressions
+--------------------------------------------------------------------------------
+
+-- Step 1: Define a view
+CREATE OR REPLACE VIEW hr.employee_view AS
+ SELECT employee_id AS EMP_ID,
+ ROUND(MONTHS_BETWEEN(SYSDATE, hire_date)/12) AS YEARS_OF_SERVICE,
+ CONCAT(UPPER(first_name), ' ', UPPER(last_name)) AS FULL_NAME,
+ EMAIL
+ FROM hr.employees;
+
+-- Step 2: Apply redaction to FIRST_NAME
+BEGIN
+ DBMS_REDACT.ALTER_POLICY(
+ object_schema => 'HR',
+ object_name => 'EMPLOYEES',
+ policy_name => 'REDACT_DATA',
+ column_name => 'FIRST_NAME',
+ action => DBMS_REDACT.ADD_COLUMN,
+ function_type => DBMS_REDACT.FULL
+ );
+END;
+/
+
+-- Step 3: Apply redaction to EMAIL using regexp
+BEGIN
+ DBMS_REDACT.ALTER_POLICY(
+ object_schema => 'HR',
+ object_name => 'EMPLOYEES',
+ policy_name => 'REDACT_DATA',
+ column_name => 'EMAIL',
+ action => DBMS_REDACT.ADD_COLUMN,
+ function_type => DBMS_REDACT.REGEXP,
+ regexp_pattern => '^.*$',
+ regexp_replace_string => 'xxxx@company.com',
+ regexp_position => 1,
+ regexp_occurrence => 1,
+ regexp_match_parameter => 'i'
+ );
+END;
+/
+
+-- Step 4: Query the view
+SELECT emp_id, years_of_service, full_name, email
+FROM hr.employee_view
+WHERE emp_id IN (101, 103, 176, 201);
+--------------------------------------------------------------------------------
+-- HR sees names/emails; non-HR sees X and xxxx@company.com
+--------------------------------------------------------------------------------
diff --git a/txeventq/kafka-connector-example/kafka_client/pom.xml b/txeventq/kafka-connector-example/kafka_client/pom.xml
index fce62ae2..f51dcd27 100644
--- a/txeventq/kafka-connector-example/kafka_client/pom.xml
+++ b/txeventq/kafka-connector-example/kafka_client/pom.xml
@@ -23,7 +23,7 @@
org.apache.kafka
kafka-clients
- 3.7.1
+ 3.9.1