must use proper version of cds-mysql
with @sap/cds
sdk
@sap/cds version | @sap/cds-mtxs version | cds-mysql version |
---|---|---|
5.8.x | 5.9.x | |
5.9.x | 5.9.x | |
6.0.x | 6.0.x | |
6.1.x | 6.1.x | |
6.2.x | 6.2.x | |
6.3.x | 6.3.x | |
6.4.x | 6.4.x | |
6.5.x | 6.5.x | |
6.6.x | 6.6.x | |
6.7.x | 1.7.x | 6.7.x |
6.8.x | 1.8.x | 6.8.x |
7.0.x | 1.9.x | 7.0.x |
7.1.x | 1.10.x | 7.1.x |
7.2.x | 1.11.x | 7.2.x |
7.3.x | 1.12.x | 7.3.x |
7.4.x | 1.13.x | 7.4.x |
7.5.x | 1.14.x | 7.5.x |
7.6.x | 1.15.x | 7.6.x |
7.7.x | 1.16.x | 7.7.x |
7.8.x | 1.17.x | 7.8.x |
7.9.x | 1.18.x | 7.9.x |
you can specify the configuration of
cds-mysql
at thecds.requires.db
node
{
"cds": {
"requires": {
"db": {
"kind": "mysql",
"csv": { "migrate": false },
"tenant": {
"deploy": {
"eager": ["default"]
}
}
},
"mysql": { "impl": "cds-mysql" }
}
}
}
interface
interface MysqlDatabaseOptions {
/**
* database credentials
*/
credentials: MySQLCredential;
/**
* tenant configuration
*/
tenant?: {
deploy?: {
/**
* auto migrate database schema when connect to it (create pool),
*
* @default true
*/
auto?: boolean;
/**
* eager deploy tenant id list
*
* schema sync of these tenants will be performed when server startup
*
* @default ['default']
*/
eager?: Array<string> | string;
/**
* eager deploy will also include tenants from cds.env.requires.auth.users
*
* @default false
*/
withMockUserTenants?: boolean;
/**
* transparent migrate, require to use `cds-mysql-build` to generate migrations.sql
*
* @default false
*/
transparent?: boolean;
};
/**
* tenant database name prefix
*/
prefix?: string;
};
/**
* mysql connection configurations
*/
connection?: {
/**
* `max_allowed_packet` size of mysql database, when create the pool of tenant, `cds-mysql` will try to set the global `max_allowed_packet` variable
*
* The value should be a multiple of 1024; non-multiples are rounded down to the nearest multiple.
*/
maxallowedpacket?: number | boolean;
};
/**
* connection pool options for each tenant
*/
pool?: PoolOptions;
/**
* csv configurations
*/
csv?: {
/**
* migrate CSV on deployment
*
* @default false
*/
migrate?: boolean;
identity?: {
/**
* `cds-mysql` will parallel to query record by keys,
* to check the record is existed or not
*/
concurrency?: number;
};
exist?: {
/**
* when `cds-mysql` found the record is existed in database
*
* update or skip that.
*
* @default false
*/
update?: boolean;
};
/**
* enhanced csv processing for `preDelivery` aspect
*
* @default false
*/
enhancedProcessing: boolean;
};
}
CDS Type | MySQL Type |
---|---|
UUID | NVARCHAR(36) |
Boolean | BOOLEAN |
UInt8 | TINYINT |
Int16 | SMALLINT |
Int32 | INTEGER |
Integer | INTEGER |
Int64 | BIGINT |
Integer64 | BIGINT |
Decimal | DECIMAL |
Double | DOUBLE |
Date | DATE |
Time | TIME |
DateTime | DATETIME |
Timestamp | DATETIME(3) |
String(LENGTH) | NVARCHAR(LENGTH) |
String | NVARCHAR(255) |
Binary | VARBINARY |
LargeBinary | LONGBLOB |
LargeString | LONGTEXT |
cds-mysql
will use the cds compiler
to generate DDL
SQL statements, then parse the DDL
statements, and convert them into typeorm
-EntitySchema
objects, then do the migration with typeorm
existed migration functionality.
graph LR
CDS[CDS Definition] --> |compile CDS to DDL| DDL[Compiled DDL]
DDL --> |ast parser| te[TypeORM Entity Metadata]
te --> |use typeorm migrate schema|Schema[Database Schema]
It will be fully automatically, sync changed columns
, views
.
It will NEVER drop old tables
/columns
, it will be SAFE in most cases.
cds-mysql
support to generate readable/trackable/auditablemigrations.sql
to perform schema migration
to enable that, please modify the cds configuration with transparent
deployment flag
{
"name": "transparent-migration",
"cds": {
"requires": {
"db": {
"kind": "mysql",
"tenant": {
+ "deploy": {
+ "transparent": true
+ }
}
}
}
}
}
then run the npx cds-mysql-build
command to generate migrations file
db/last-dev/mysql.json
- store the latest modeldb/migrations.sql
- store the migration statements between different versions
an example of migrations.sql
-- generated by cds-mysql
-- database migration scripts
-- do not manually change this file
-- version: 100 hash: 89d52ab9f80a0fb38b9d52bc1caeeaf532d208e4b5671818830f4fa2032c45d1 at: 2023-02-01T12:03:22.489Z
CREATE TABLE `FioriService_Forms_drafts` (`ID` varchar(36) NOT NULL, `f1` varchar(255) NULL, `f2` varchar(255) NULL, `f3` int NULL, `f4` decimal NULL, `IsActiveEntity` tinyint NOT NULL DEFAULT 1, `HasActiveEntity` tinyint NOT NULL DEFAULT 0, `HasDraftEntity` tinyint NOT NULL DEFAULT 0, `DraftAdministrativeData_DraftUUID` varchar(36) NULL, PRIMARY KEY (`ID`, `IsActiveEntity`)) ENGINE=InnoDB CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'
-- <remove some statements here> ...
-- version: 101 hash: 06962d18b51963e273375acc512ea8e96b69325620662a78d57b4cf9c2bc1877 at: 2023-02-01T12:04:16.622Z
DROP VIEW `FioriService_Persons`
ALTER TABLE `FioriService_Persons_drafts` ADD `Country` varchar(40) NULL DEFAULT 'CN'
ALTER TABLE `test_resources_fiori_db_Person` ADD `Country` varchar(40) NULL DEFAULT 'CN'
CREATE VIEW `FioriService_Persons` AS SELECT
Person_0.ID,
Person_0.Name,
Person_0.Age,
Person_0.Address,
Person_0.Country
FROM test_resources_fiori_db_Person AS Person_0;
-- version: 102 hash: c7f16bf48eb23fe3f5b52c53c53bfade82a3ce2f3a6532493b82d16cefa75e37 at: 2023-02-04T03:41:18.704Z
CREATE TABLE `cds_xt_Extensions` (`ID` varchar(36) NOT NULL, `tag` text NULL, `csn` longtext NULL, `i18n` longtext NULL, `sources` longblob NULL, `activated` text NULL, `timestamp` datetime(3) NULL, PRIMARY KEY (`ID`)) ENGINE=InnoDB CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'
NOTE: mtxs - extensibility
CAN NOT work with transparent migration
Out-of-Box multi-tenancy support
- develop the single tenant application, use the
default
as tenant id - develop the multi-tenancy application, fill the
User.tenant
information for eachrequest
/event
, andcds-mysql
will automatically sync schema/CSV and provision connection pool for that tenant- data isolation in mysql database level, each tenant will own its own
database
- better to create a
admin
user tocds-mysql
so thatcds-mysql
could help you to createdatabase
- data isolation in mysql database level, each tenant will own its own
- multi-tenancy could work without
@sap/mtxs
, but if enable the@sap/mtxs
features, please find more details at MTXS documentation
cds-mysql
has a built-in csv migrator, it will migrate data with key validation.
- if key of entity is existed, depends on the
cds.requires.db.csv.exist.update
, if the value istrue
, try to update, otherwise will skip the record - if key of entity not existed, insert (if the records has been deleted, its also will be inserted)
- for
clob
columns (likecds.Binary
/cds.LargeBinary
), please fill csv withbase64
encoded value - for
array of
columns, please fill with JSON string with correct CSV encoding, for example, use""
two double quotes to represent"
double quotes.
support
UPSERT
with mysqlINSERT ... ON DUPLICATE KEY UPDATE
feature
- ONLY could be handled by
DatabaseService
, there is no builtin handler are registered forcds.ApplicationService
UPSERT
will not return the updated object- till now, there are some features not well-implemented by cds team, for example, not able to automatically rewrite
upsert
forview
module.exports = class DemoService extends cds.ApplicationService {
async _upsert(req) {
const Product = "test.upsert.Product";
const { data } = req;
await cds.run(cds.ql.UPSERT.into(Product).entries(data));
return cds.run(cds.ql.SELECT.one.from(Product).where({ ID: data.ID }));
}
};
CREATE
andDROP
ql are DISABLED bycds-mysql
.
it means:
cds.run(CREATE.entity(def)); // with throw error 'ERR_NOT_SUPPORT_CQN_CREATE'
if you have the blob
column, and try to upload large file/binary, maybe will encounter the ER_NET_PACKET_TOO_LARGE
server side error, you can configure the max_allowed_packet
in server side, or configure the global variable by cds-mysql
(will be restore after mysql restart)
{
"cds": {
"requires": {
"db": {
"kind": "mysql",
"connection": {
"maxallowedpacket": 104857600 // 100 MB
}
},
"kinds": {
"mysql": {
"impl": "cds-mysql"
}
}
}
}
}
cds-mysql
setup pool for EACH tenant, for more options of pool, please ref options section of generic-pool
{
"cds": {
"requires": {
"db": {
"kind": "mysql",
"pool": {
"max": 50
}
},
"kinds": {
"mysql": {
"impl": "cds-mysql"
}
}
}
}
}
cds-mysql
will automatically migrate schema and pre-defined CSV data into database when connecting to database (generally it means server received the first request which need database operation).
just specify the
requires.db.tenant.deploy.eager
to sync schema (of target tenants) at startup
{
"cds": {
"requires": {
"db": {
"tenant": {
"deploy": {
"eager": ["default", "<a-tenant-id here>"]
}
}
}
}
}
}
define entity with
incrementalID
aspect to support theAUTO_INCREMENT
syntax inmysql
db
NOT COMPATIBLE with deep composition/association operations, its better to use the
@sap/cds/common - cuid
aspect for deep operations
using {incrementID} from 'cds-mysql';
// the entity `Animal` will have an auto-filled 'ID' field
// ONLY support single record insert
entity Animal : incrementID {
Name : String(255);
}
csv migrator will automatically fill the
PreDelivery
field astrue
for business, if user want to delete some data, just set theDisabled
field astrue
content hash will be checked before provisioned, if one file has been filled before,cds-mysql
will skip processing it.
using {incrementID, preDelivery} from 'cds-mysql';
entity Person : incrementID, preDelivery {
Name : String(255);
}
then use the database model with filter
// where Disabled = false
entity Peoples as projection on db.Person excluding {
PreDelivery,
Disabled
} where Disabled = false;
if enable the enhancedProcessing
options
{
"cds": {
"requires": {
"db": {
"kind": "mysql",
"csv": {
"migrate": true,
"exist": {
"update": true
},
+ "enhancedProcessing": true
}
}
}
}
}
NOTE: cds-mysql
will reject db DELETE
operations for pre-delivery = true
records
{
"error": {
"@Common.numericSeverity": 4,
"code": "400",
"message": "ERR_DELETE_PRE_DELIVERED_DATA"
}
}
cds-mysql
allow user define entity with mysql built-in index
@cds.typeorm.config : {indices : [{
name : 'ProductName', // key name
columns : ['Name'] // index fields
}]}
entity Product : cuid {
Name : TranslatedText;
Price : Decimal(10, 2);
}
NOTE: index
migration is not well-tested, carefully use that on your own risk.
as CAP supported, developer could also use
environments
to configure the database credential
CDS_REQUIRES_DB_CREDENTIALS_USER=cds_admin
CDS_REQUIRES_DB_CREDENTIALS_PASSWORD=cds_admin
CDS_REQUIRES_DB_CREDENTIALS_DATABASE=cds_admin
CDS_REQUIRES_DB_CREDENTIALS_HOST=127.0.0.1
CDS_REQUIRES_DB_CREDENTIALS_PORT=3306
create a default-env.json
file into the root directory of your CAP project, it will be useful if you want to put some details information of mysql driver
for more supported options in credentials
node, please ref the mysql official connection options document
{
"VCAP_SERVICES": {
"user-provided": [
{
"label": "user-provided",
"name": "remote-mysql-service",
"tags": ["mysql"],
"credentials": {
"host": "mysql.host.name.com",
"user": "user",
"password": "cdsPas$w0rd",
"database": "test",
"port": 3306
}
}
]
}
}
if you want to run cds-mysql on cloud foundry for production
create mysql service by cf cups
with following format
cf cups remote-mysql-service -t 'mysql' -p '{"host":"public.mysql.instance.com","user":"cds-user","password":"CdsUser123$","database":"cds-user","port":3306,"ssl":{"ca":"-----BEGIN CERTIFICATE-----\n ......\n-----END CERTIFICATE-----\n"}}'
you can convert PEM cert to json format with this document, just run command
awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' cert-name.pem
for the database user which configured in credential, at least, it should have the permission to perform SQL and DML.
if the multi-tenancy
or mtxs
is enabled, the user need the permission to CREATE/DROP DATABASE
and other DDL permission.
hard restrictions from database
- the maximum length of a table name is 64 characters - so the
length of entity name with namespace
cannot exceed 64 chars - The internal representation of a MySQL table has a maximum row size limit of
65,535
bytes. - upload attachment maybe will meet
max_allowed_packet
issue, it can be configured on server side. - MySQL does not support entities with parameters
some restrictions from
cds-mysql
implementation
date
column not support default value$now
- The
Boolean
type is represented asTINYINT(1)
in mysql server, as a result,boolean default true/false
will be converted toTINYINT DEFAULT 1/0
. - The
incrementID
aspect ofmysql
, does not work withmanaged composition
, becausemysql
do not supportcomposite primary key
contains anauto_increment
column