Skip to content

Commit

Permalink
Database: Added support for ST classes: Float, Date, Boolean, Uint8Array
Browse files Browse the repository at this point in the history
Note 1: The columns method in SqlObject subclasses now also need to specify the column class.
See MyType.st in Node project for an example and Database/Database.md for documentation.
Note 2: In SqlTable subclasses methods named 'query*' are renamed to 'select*'.
Updated npm packages.
  • Loading branch information
FunctionPoint committed Jan 19, 2025
1 parent 83283b7 commit c873569
Show file tree
Hide file tree
Showing 61 changed files with 1,128 additions and 792 deletions.
14 changes: 7 additions & 7 deletions Compiler/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Compiler/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"type": "module",
"dependencies": {
"@types/node": "^22.10.5",
"@types/node": "^22.10.7",
"source-map": "^0.7.4"
}
}
2 changes: 1 addition & 1 deletion Compiler/src/Compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export class Compiler

version()
{
console.log( "SmallJS version: " + "1.4.0" );
console.log( "SmallJS version: " + "1.5.0" );
}

compile( inputFolders: string[], outputFolder: string )
Expand Down
74 changes: 43 additions & 31 deletions Database/Database.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# SmallJS database support

SmallJS currently supports these databases: Postgres, MariaDB, MySQL and SQLite.
Note that using MySQL in commercial software requires a payed license.
Note that using MySQL in commercial software requires a paid license.

# Resources

Expand All @@ -13,24 +13,24 @@ The these can be installed with their default settings.

## Postgres

- Server: https://www.postgresql.org/download/
- Admin: https://www.pgadmin.org/download/
- Server: https://www.postgresql.org/download/
- Admin: https://www.pgadmin.org/download/

## MariaDB

- Server: https://mariadb.org/download/
- Admin: https://www.heidisql.com/download.php
- Server: https://mariadb.org/download/
- Admin: https://www.heidisql.com/download.php

## MySQL

- Server: https://dev.mysql.com/downloads/mysql/
- Admin: https://dev.mysql.com/downloads/workbench/
- Server: https://dev.mysql.com/downloads/mysql/
- Admin: https://dev.mysql.com/downloads/workbench/

## SQLite

- Server: Is built into Nodes.js starting v 22.5.0,
Use a current version of node (v23.5.0+) that does *not* need this startup: "--experimental-sqlite".
- Admin: https://sqlitebrowser.org/dl/
- Server: Is built into Nodes.js starting v 22.5.0,
Use a current version of node (v23.5.0+) that does *not* need this startup: "--experimental-sqlite".
- Admin: https://sqlitebrowser.org/dl/

# Create the SmallJS database

Expand Down Expand Up @@ -59,33 +59,43 @@ This base class has an 'id' variable that identifies the object in the correspon
So that table must have an 'id' column of type "autonumber", that is the primary key.

A subclass of SqlObject, say Product, should overload the method 'columns'
to return an array of column names in the SQL table next to 'id'
then are also equal to variable names in the SqlObject subclass.
to return an array of column descriptors each with a subarray of 2 items:
1 name: name of the SQL column name and the same accessor (also variable) in the subclass of SqlObject.
2 type: The desired Smalltalk class of the column.
- E.g. for Product.columns: ^ #( #( 'name' String ) #( 'price' Integer ) )'.

- E.g. for Product.columns: ^ #( 'name' 'price' )'.
These Smalltalk types are supported by the ORM:
String, Integer, Float, Date, Boolean, Uint8Array (binary / BLOB), Nil (null).

See class TestDabase in the ./Node workspace for this and following examples.
The following conversions are done due to limited database (driver) type support:
ST Date is stored as an SQL String for all databases.
This is because SQLite does not support a SQL Timestamp type,
and other databases have limited precision on it,
making a source JS Date not equal to the SQL Timestamp when retrieved.
ST Boolean to SQL Integer for: SQLite, MariaDB.

See class TestDatabase in the ./Node workspace for this and following examples.

## Connecting to a database

Connecting to a database is done with this line of ST code:

database := PostgresDatabase new connect: connectionString then: [ self onConnect ].
database := PostgresDatabase new connect: connectionString then: [ self onConnect ].

where connectionString is something like:

'postgres://postgres:password@localhost:5432/smalljs
'postgres://postgres:password@localhost:5432/smalljs

For different databases please PostgresDatabase with MariadbDatabase or MysqlDatabase
For different databases replace PostgresDatabase with MariadbDatabase, MysqlDatabase or SqliteDatabase.

## Connect a database table to a ST class with ORM

Then connect to a table with code ST like:

productTable := database table: 'public."Product"' rowClass: MyProduct.
productTable := database table: 'public."Product"' rowClass: MyProduct.

Note: Tables should exist already, the ORM does not support creating them.
A "database first" approach is taken, to keep the SQL clean and reusable elsewhere.
A "database first" approach is taken, to keep the SQL clean and reusable elsewhere.

## Create, Read, Update, Delete (CRUD) operations

Expand All @@ -95,33 +105,33 @@ The following code examples implement CRUD operations:

Insert new product, setting its id:

product := MyProduct new name: 'Test'.
productTable insert: product then: [ :product | ... ].
product := MyProduct new name: 'Test'.
productTable insert: product then: [ :product | ... ].

### Read (select query)

Read multiple products by price:

productTable query: 'price >= 100' then: [ :products | ... ].
productTable select: 'price >= 100' then: [ :products | ... ].

Read single product by Id:

productTable queryId: 1 then: [ :product | ... ].
productTable selectId: 1 then: [ :product | ... ].

If the id does not exist, nil passed to the then block.

### Update

Update a product using its id:

product name: 'Changed name'.
productTable update: product then: [ ... ].
product name: 'Changed name'.
productTable update: product then: [ ... ].

### Delete

Delete a product, using its id:

productTable delete: product then: [ ... ].
productTable delete: product then: [ ... ].

## Using a database without ORM

Expand All @@ -130,14 +140,16 @@ Note that these are always called queries, even if they contain mutation command

To query a Postgres table for which there is no SqlObject mapping, you can use:

database query: 'SELECT id, name FROM "Product" WHERE id = 1'
then: [ :queryResult | self onProductQuery: queryResult ].
database query: 'SELECT id, name FROM "Product" WHERE id = 1'
then: [ :queryResult | self onProductQuery: queryResult ].

And then in "onProductQuery: queryResult" read the results with:

"queryResult fieldNames" returns: #( 'id' 'name' 'price' ).
"queryResult rows first atJsProperty: 'name'" returns: 'Apple'.
"queryResult fieldNames" returns: #( 'id' 'name' 'price' ).
"queryResult rows first atJsProperty: 'name'" returns: 'Apple'.

Then using a Postgress database, "queryResult" with be an instance of the class PostgresQueryResult.
Then using a Postgres database, "queryResult" with be an instance of the class PostgresQueryResult.
Other databases will have other implementation details.
To be (more) database independent, it is recommended to use the ORM mapping functionality described above.


21 changes: 16 additions & 5 deletions Database/MariaDB/SmallJS.sql
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@


-- Dumping database structure for smalljs
DROP DATABASE IF EXISTS `smalljs`;
CREATE DATABASE IF NOT EXISTS `smalljs` /*!40100 DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci */;
USE `smalljs`;

-- Dumping structure for table smalljs.order
DROP TABLE IF EXISTS `order`;
CREATE TABLE IF NOT EXISTS `order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`person` int(11) NOT NULL,
Expand All @@ -41,15 +39,14 @@ INSERT INTO `order` (`id`, `person`, `product`, `amount`) VALUES
(3, 2, 3, 2);

-- Dumping structure for table smalljs.person
DROP TABLE IF EXISTS `person`;
CREATE TABLE IF NOT EXISTS `person` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL,
`password` varchar(64) DEFAULT NULL,
`salt` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=508 DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci;
) ENGINE=InnoDB AUTO_INCREMENT=641 DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci;

-- Dumping data for table smalljs.person: ~3 rows (approximately)
INSERT INTO `person` (`id`, `name`, `password`, `salt`) VALUES
Expand All @@ -58,7 +55,6 @@ INSERT INTO `person` (`id`, `name`, `password`, `salt`) VALUES
(3, 'Robert', '35e2b3530c3b7b2c7c0ae6670a1a6eb60a2e9e57c3c393f4a0ceaf11e71351ff', 484813);

-- Dumping structure for table smalljs.product
DROP TABLE IF EXISTS `product`;
CREATE TABLE IF NOT EXISTS `product` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL,
Expand All @@ -73,6 +69,21 @@ INSERT INTO `product` (`id`, `name`, `price`) VALUES
(2, 'Orange', 150),
(3, 'Mango', 220);

-- Dumping structure for table smalljs.type
CREATE TABLE IF NOT EXISTS `type` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`string` text CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
`integer` int(11) NOT NULL,
`float` double NOT NULL,
`date` text NOT NULL,
`boolean` tinyint(4) NOT NULL,
`binary` blob NOT NULL,
`anil` int(11) DEFAULT NULL,
KEY `id` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci;

-- Dumping data for table smalljs.type: ~0 rows (approximately)

/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;
Expand Down
34 changes: 32 additions & 2 deletions Database/MySQL/SmallJS.sql
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ CREATE TABLE `person` (
`salt` int DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `Name_UNIQUE` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1;
) ENGINE=InnoDB AUTO_INCREMENT=130 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
Expand Down Expand Up @@ -99,6 +99,36 @@ LOCK TABLES `product` WRITE;
INSERT INTO `product` VALUES (1,'Apple',100),(2,'Orange',150),(3,'Mango',220);
/*!40000 ALTER TABLE `product` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `type`
--

DROP TABLE IF EXISTS `type`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `type` (
`id` int NOT NULL AUTO_INCREMENT,
`string` varchar(80) NOT NULL,
`integer` int NOT NULL,
`float` double NOT NULL,
`date` varchar(80) NOT NULL,
`boolean` tinyint NOT NULL,
`binary` blob NOT NULL,
`anil` int DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id_UNIQUE` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `type`
--

LOCK TABLES `type` WRITE;
/*!40000 ALTER TABLE `type` DISABLE KEYS */;
/*!40000 ALTER TABLE `type` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
Expand All @@ -109,4 +139,4 @@ UNLOCK TABLES;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2024-12-24 20:13:50
-- Dump completed on 2025-01-19 15:43:08
Loading

0 comments on commit c873569

Please sign in to comment.