{"id":13770980,"url":"https://github.com/svenvc/P3","last_synced_at":"2025-05-11T03:32:59.334Z","repository":{"id":22185628,"uuid":"95534378","full_name":"svenvc/P3","owner":"svenvc","description":"A lean and mean PostgreSQL client for Pharo","archived":false,"fork":false,"pushed_at":"2025-01-07T12:47:12.000Z","size":446,"stargazers_count":75,"open_issues_count":4,"forks_count":21,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-04-06T06:13:51.326Z","etag":null,"topics":["database-driver","glorp","pharo","postgresql","smalltalk"],"latest_commit_sha":null,"homepage":null,"language":"Smalltalk","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/svenvc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-06-27T08:14:32.000Z","updated_at":"2025-01-07T12:47:15.000Z","dependencies_parsed_at":"2022-09-20T05:51:40.101Z","dependency_job_id":"5646248e-1458-45aa-acc4-7603016b12ed","html_url":"https://github.com/svenvc/P3","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/svenvc%2FP3","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/svenvc%2FP3/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/svenvc%2FP3/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/svenvc%2FP3/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/svenvc","download_url":"https://codeload.github.com/svenvc/P3/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253514352,"owners_count":21920327,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["database-driver","glorp","pharo","postgresql","smalltalk"],"created_at":"2024-08-03T17:00:45.876Z","updated_at":"2025-05-11T03:32:58.862Z","avatar_url":"https://github.com/svenvc.png","language":"Smalltalk","readme":"# P3\n\nP3 is a modern, lean and mean PostgreSQL client for Pharo.\n\n[![CI](https://github.com/svenvc/P3/actions/workflows/CI.yml/badge.svg)](https://github.com/svenvc/P3/actions/workflows/CI.yml)\n\n**P3Client** uses frontend/backend protocol 3.0 (PostgreSQL version 7.4 [2003] and later),\nimplementing the simple and extended query cycles.\nIt supports plaintext, md5 and scram-sha-256 password authentication.\nWhen SQL queries return row data, it efficiently converts incoming data to objects.\nP3Client supports most common PostgreSQL types.\n\nP3Client can be configured manually or through a URL.\n\n```smalltalk\nP3Client new url: 'psql://username:password@localhost:5432/databasename'.\n```\n\nNot all properties need to be specified, the minimum is the following URL.\n\n```smalltalk\nP3Client new url: 'psql://user@localhost'.\n```\n\nP3Client has a minimal public protocol, basically #query: (#execute: is an alias).\n\nOpening a connection to the server (#open) and running the authentication\nand startup protocols (#connect) are done automatically when needed from #query.\n\nP3Client also supports SSL connections. Use #connectSSL to initiate such a connection.\nAlternatively you can add `sslmode=require` to the connection URL, as in\n`'psql://username:password@localhost:5432/databasename?sslmode=require'`.\n\nThrough the #prepare: message, you can ask P3Client to prepare/parse an SQL statement or\nquery with parameters. This will give you a P3PreparedStatement instance than you can then\nexecute with specific parameters. Polymorphic to this there is also P3FormattedStatement\nwhich you create using the #format: message. These work at the textual, client side level.\n\n## Basic Usage\n\nHere is the simplest test that does an actual query, it should return true.\n\n```smalltalk\n(P3Client new url: 'psql://sven@localhost') in: [ :client |\n   [ client isWorking ] ensure: [ client close ] ].\n```\n\nThis is how to create a simple table with some rows in it.\n\n```smalltalk\n(P3Client new url: 'psql://sven@localhost') in: [ :client |\n   client execute: 'DROP TABLE IF EXISTS table1'.\n   client execute: 'CREATE TABLE table1 (id INTEGER, name TEXT, enabled BOOLEAN)'.\n   client execute: 'INSERT INTO table1 (id, name, enabled) VALUES (1, ''foo'', true)'.\n   client execute: 'INSERT INTO table1 (id, name, enabled) VALUES (2, ''bar'', false)'.\n   client close ].\n```\n\nNow we can query the contents of the simple table we just created.\n\n```smalltalk\n(P3Client new url: 'psql://sven@localhost') in: [ :client |\n   [ client query: 'SELECT * FROM table1' ] ensure: [ client close ] ].\n```\n\nThe result is an instance of P3Result\n\n```smalltalk\n   a P3Result('SELECT 2' 2 records 3 columns)\n```\n\nP3Result contains 3 elements, results, descriptions \u0026 data:\n\n- Results is a string (collection of strings for multiple embedded queries) indicating successful execution.\n- Descriptions is a collection of row field description objects.\n- Data is a collection of rows with fully converted field values as objects.\n\nThe data itself is an array with 2 sub arrays, one for each record.\n\n```smalltalk\n#( #(1 'foo' true) #(2 'bar' false) )\n```\n\nFinally we can clean up.\n\n```smalltalk\n(P3Client new url: 'psql://sven@localhost') in: [ :client |\n   [ client execute: 'DROP TABLE table1' ] ensure: [ client close ] ].\n```\n\n## References\n\n-  https://postgresql.org\n-  https://en.wikipedia.org/wiki/PostgreSQL\n-  https://www.postgresql.org/docs/9.6/static/protocol.html\n\n## Using Prepared and Formatted Statements\n\nAlthough you are free to create your SQL statements in any way you see fit,\nfeeding them to #execute: and #query:,\ninserting arguments in SQL statements can be hard (because you have to know the correct syntax),\nerror-prone (because you might violate syntax rules) and dangerous (due to SQL injection attacks).\n\nP3 can help here with two mechanisms: prepared and formatted statements.\nThey are mostly polymorphic and use the same template notation.\nThey allow you to create a statement once, specifying placeholders with $n,\nand execute it once or multiple times with concrete arguments,\nwith the necessary conversions happening automatically.\n\nThe difference between the two is that formatted statements are implemented\nusing simple textual substitution on the client side, while\nprepared statements are evaluated on the server side with full syntax checking,\nand are executed with more type checks.\nPrepared statements are more efficient since the server can do part of its optimization\nin the prepare phase, saving time on each execution.\n\nHere is a transcript of how to use them. First we set up a client and create a test table.\n\n```smalltalk\nclient := P3Client new url: 'psql://sven@localhost'.\n\nclient execute: 'DROP TABLE IF EXISTS table1'.\nclient execute: 'CREATE TABLE table1 (id INTEGER, name TEXT, weight REAL, enabled BOOLEAN)'.\n```\n\nNext we insert some data and then query it using prepared statements.\n\n```smalltalk\nstatement := client prepare: 'INSERT INTO table1 (id, name, weight, enabled) VALUES ($1, $2, $3, $4)'.\n\nstatement execute: { 1. 'foo'. 75.5. true }.\nstatement executeBatch: { { 2. 'bar'. 80.25. true }. { 3. 'foobar'. 10.75. false } }.\n\nstatement close.\n\nstatement := client prepare: 'SELECT id, name, weight FROM table1 WHERE id = $1 AND enabled = $2'.\nstatement query: { 1. true }.\n\nstatement close.\n```\n\nNote that prepared statements are server side resources that need to be closed when no longer needed.\nPrepared statements exist in the scope of a single session/connection.\n\nNext we start over and do the same insert and query using formatted statements.\n\n```smalltalk\nclient execute: 'TRUNCATE TABLE table1'.\n\nstatement := client format: 'INSERT INTO table1 (id, name, weight, enabled) VALUES ($1, $2, $3, $4)'.\n\nstatement execute: { 1. 'foo'. 75.5. true }.\nstatement executeBatch: { { 2. 'bar'. 80.25. true }. { 3. 'foobar'. 10.75. false } }.\n\nstatement := client format: 'SELECT id, name, weight FROM table1 WHERE id = $1 AND enabled = $2'.\nstatement query: { 1. true }.\n```\n\nAnd finally we clean up.\n\n```smalltalk\nclient execute: 'DROP TABLE table1'.\nclient close.\n```\n\n## Supported Data Types\n\nP3 supports most common PostgreSQL types. Here are some tables with the details.\nAs of PostgreSQL 9.6, there are 41 general purpose data types of which 32 are currently implemented.\n\nThese are the 32 general purpose data type currently implemented,\nwith the Pharo class they map to.\n\nName | Alias | Description | Oid | Class \n-----|-------|-------------|-----|------\nbigint | int8 | signed eight-byte integer | 20 | Integer\nbigserial | serial8 | autoincrementing eight-byte integer | 20 | Integer\nbit [n] | | fixed-length bit string | 1560 | P3FixedBitString\nbit varying | varbit | variable-length bit string | 1562 | P3BitString\nboolean | bool | logical boolean (true/false) | 16 | Boolean\nbox | | rectangular box on a plane (upperright, lowerleft) | 603 | P3Box\nbytea | | binary data (byte array) | 17 | ByteArray\ncharacter [n] | char | fixed-length character string | 1042 | String\ncharacter varying | varchar | variable-length character string | 1043 | String\ncircle | | circle on a plane (center, radius) | 718 | P3Circle\ndate | | calendar date (year,month,day) | 1082 | Date\ndouble precision | float8 | double precision floating point number (8 bytes) | 701 | Float\ninteger | int, int4 | signed four-byte integer | 23 | Integer\ninterval | | time span | 114 | P3Interval\njson | | textual JSON data | 114 | NeoJSONObject\njsonb | | binary JSON data, decomposed | 3802 | NeoJSONObject\nline | | infinite line on a plane (ax+by+c=0) | 628 | P3Line\nlseg | | line segment on a plane (start,stop) | 601 | P3LineSegment\nnumeric | decimal | exact number of selectable precision | 1700 | ScaledDecimal\npath | | geometric path on a plane (points) | 602 | P3Path\npoint | | geometric point on a plane (x, y) | 600 | P3Point\npolygon | | closed geometric path on a plane (points) | 604 | P3Polygon\nreal | float4 | single-precision floating point number (4-bytes) | 700 | Float\nsmallint | int2 | signed two-byte integer | 21 | Integer\nsmallserial | serial2 | autoincrementing two-byte integer | 21 | Integer\nserial | serial4 | autoincrementing four-byte integer | 23 | Integer\ntext | | variable-length character string | 25 | String\ntime [ without time zone ] | | time of day (no time zone) | 1083 | Time\ntime with time zone | timetz | time of day including time zone | 1266 | Time\ntimestamp [ without time zone ] | | date and time (no time zone) | 1114 | DateAndTime\ntimestamp with time zone  | timestamptz | date and time includig time zone | 1184 | DateAndTime\nuuid | | universal unique identifier | 2950 | UUID\n\nHere are the 9 general purpose data types that are not yet implemented.\n\nName | Description | Oid\n-----|-------------|----\ncidr | IPv4 or IPv6 network address | 650\ninet | IPv4 or IPv6 host address | 869\nmacaddr | MAC (Media Access Control) address | 829\nmoney | currency amount | 790\npg_lsn | PostgreSQL Log Sequence Number | 3220\ntsquery | text search query | 3615\ntsvector | text search document | 3614\ntxid_snapshot | user-level transaction ID snapshot | 2970\nxml | XML data | 142\n\nAdditionally, the following 10 common types are also implemented,\nwith the Pharo class they map to.\n\nName | Description | Oid | Class\n-----|-------------|-----|------\noid | object identifier | 26 | Integer\nname | name | 19 | String\nbpchar | text | 1042 | String\nvoid | void | 2278 | UndefinedObject\n_bool | boolean array | 1000 | Array\\\u003cBoolean\\\u003e\n_int4  | integer array | 1007 | Array\\\u003cInteger\\\u003e\n_oid | oid array | 1028 | Array\\\u003cInteger\\\u003e\n_text | string array | 1009 | Array\\\u003cString\\\u003e\n_varchar | string array | 1015 | Array\\\u003cString\\\u003e\n_float8 | float array | 1022 | Array\\\u003cFloat\\\u003e\n\nP3 also supports enums. Each enum definition creates a new type.\nYou can send #loadEnums to P3Client to create mappings for all visible enums.\n\nWhen you do a query that results in data of an unknown type you will get an error,\nP3 cannot convert typeOid XXX, where XXX is the oid in the pg_type table.\n\n## Connection and Authentication\n\nP3 connects over the network (TCP) to PostgreSQL and\nsupports plain (#connect) and TLS/SSL encrypted (#connectSSL) connections.\n\nIt is out of the scope of this README to explain how to install and configure\nan advanced database like PostgreSQL. There is extensive high quality documentation\navailable covering all aspect of PostgreSQL, see https://postgresql.org\n\nOut of the box, most PostgreSQL installations do not allow for network connections\nfrom other machines, only for local connections.\nCheck the listen_addresses directive in postgresql.conf\n\nAs for authentication, CleartextPassword, MD5Password and SCRAM-SHA-256 are supported.\nThis means that SCMCredential, GSS, SSPI are currently not (yet) supported.\nAn error will be signalled when the server requests an unsupported authentication.\n\nYou have to create database users, called roles and give them a password.\nIn SQL you can do this with CREATE|ALTER ROLE user1 LOGIN PASSWORD 'secret'\n\nNext you have to tell PostgreSQL how network users should authenticate themselves.\nThis is done by editing pg_hba.conf choosing specific methods,\ntrust (no password, no authentication), password, md5 and scram-sha-256 work with P3.\n\nNote that for SCRAM-SHA-256 to work, you need to change the password_encryption\ndirective in postgresql.conf to scam-sha-256, restart and reenter all user passwords.\n\n## Glorp\n\nIncluded is **P3DatabaseDriver**, an interface between Glorp, an advanced object-relational mapper, and P3Client.\n\nTo install this driver (after loading Glorp itself), do\n\n```smalltalk\nPharoDatabaseAccessor DefaultDriver: P3DatabaseDriver.\n```\n\nConfigure your session using a Glorp Login object\n\n```smalltalk\nLogin new\n   database: PostgreSQLPlatform new;\n   username: 'username';\n   password: 'password';\n   connectString: 'host:5432_databasename';\n   encodingStrategy: #utf8;\n   yourself.\n```\n\n## Code loading\n\nThe default group loads P3Client and its basic dependencies NeoJSON and ZTimestamp\n\n```smalltalk\nMetacello new\n   baseline: 'P3';\n   repository: 'github://svenvc/P3';\n   load.\n```\n\nThe glorp group loads P3DatabaseDriver and the whole of Glorp (warning: large download)\n\n```smalltalk\nMetacello new\n   baseline: 'P3';\n   repository: 'github://svenvc/P3';\n   load: 'glorp'.\n```\n\n## Unit tests\n\n**P3ClientTest** holds unit tests for the P3 PSQL client.\n\nConfigure it by setting its class side's connection URL.\n\n```smalltalk\nP3ClientTest url: 'psql://sven:secret@localhost:5432/database'.\n```\n\nThe minimal being the following:\n\n```smalltalk\nP3ClientTest url: 'psql://sven@localhost'.\n```\n\n## Logging\n\nP3 uses object logging, an advanced form of code instrumentation.\nThis means that during execution instances of subclasses of P3LogEvent\nare created (some including timing information)\nand sent to an Announcer (accessible via P3LogEvent announcer).\nInterested parties can subscribe to these log events\nand use the information contained in them\nto learn about P3 code execution.\n\nThe standard print method of a P3LogEvent can be used to generate textual output.\nThe following expression enables logging to the Transcript.\n\n```smalltalk\nP3LogEvent logToTranscript.\n```\n\nExecuting the four expressions of the Basic Usage section yields the following output.\n\n```bash\n2020-09-21 16:27:57 001 [P3] 63731 #Connect sven@localhost:5432 Trust\n2020-09-21 16:27:57 002 [P3] 63731 #Query SELECT 565 AS N\n2020-09-21 16:27:57 003 [P3] 63731 #Result SELECT 1, 1 record, 1 colum, 4 ms\n2020-09-21 16:27:57 004 [P3] 63731 #Close\n\n2020-09-21 16:28:07 005 [P3] 63733 #Connect sven@localhost:5432 Trust\n2020-09-21 16:28:07 006 [P3] 63733 #Query DROP TABLE IF EXISTS table1\n2020-09-21 16:28:07 007 [P3] 63733 #Error P3Notification PostgreSQL table \"table1\" does not exist, skipping\n2020-09-21 16:28:07 008 [P3] 63733 #Result DROP TABLE, 6 ms\n2020-09-21 16:28:07 009 [P3] 63733 #Query CREATE TABLE table1 (id INTEGER, name TEXT, enabled BOOLEAN)\n2020-09-21 16:28:07 010 [P3] 63733 #Result CREATE TABLE, 50 ms\n2020-09-21 16:28:07 011 [P3] 63733 #Query INSERT INTO table1 (id, name, enabled) VALUES (1, 'foo', true)\n2020-09-21 16:28:07 012 [P3] 63733 #Result INSERT 0 1, 4 ms\n2020-09-21 16:28:07 013 [P3] 63733 #Query INSERT INTO table1 (id, name, enabled) VALUES (2, 'bar', false)\n2020-09-21 16:28:07 014 [P3] 63733 #Result INSERT 0 1, 0 ms\n2020-09-21 16:28:07 015 [P3] 63733 #Close\n\n2020-09-21 16:28:20 016 [P3] 63737 #Connect sven@localhost:5432 Trust\n2020-09-21 16:28:20 017 [P3] 63737 #Query SELECT * FROM table1\n2020-09-21 16:28:20 018 [P3] 63737 #Result SELECT 2, 2 records, 3 columns, 2 ms\n2020-09-21 16:28:20 019 [P3] 63737 #Close\n\n2020-09-21 16:39:52 020 [P3] 63801 #Connect sven@localhost:5432 Trust\n2020-09-21 16:39:52 021 [P3] 63801 #Query DROP TABLE table1\n2020-09-21 16:39:52 022 [P3] 63801 #Result DROP TABLE, 13 ms\n2020-09-21 16:39:52 023 [P3] 63801 #Close\n```\n\nRemember that the information inside the log events can be used to build other applications.\n\n## Development, Goals, Contributing\n\nThe main goal of P3 is to be a modern, lean and mean PostgreSQL client for Pharo.\nRight now, P3 is functional and usable.\n\nThe quality of open source software is determined by it being alive, supported and maintained.\n\nThe first way to help is to simply use P3 in your projects and tells us about\nyour successes and the issues that you encounter.\nYou can ask questions on the Pharo mailing lists.\n\nDevelopment happens on GitHub, where you can create issues.\n\nContributions should be done with pull requests solving specific issues.\n","funding_links":[],"categories":["Databases"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsvenvc%2FP3","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsvenvc%2FP3","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsvenvc%2FP3/lists"}