1596300660

# Functional Programming Series (2): What Is a Monoid?

For those interested in functional programming, I’ll talk about monoids and why they’re very important to understand ahead of time.

Don’t get confused: This isn’t monad — it’s monoid. I’m pretty sure you already know of monoids and you use them almost every day — you just didn’t know the term for them.

# Identity Function

Let’s assume there’s a function named `identity` that takes `A` and returns `A`.

``````const identity: <A>(a: A): A => a;

interface Student {
name: string;
age: number;
}
identity<number>(3) // 3
identity<string>('hello') // hello
identity<Student>({
name: 'Bincent',
age: 5
}); // { name: 'Bincent', age: 5 }
``````

In functional programming, this useless function (seems useless) is an important factor for many other concepts (such as monoids) that we’re about to talk about.

Basically, a monoid is a set of elements that holds the rules of the semigroup and the identity-element rule.

If `S` is a set of elements, `a` is a member of `S`, and `·` is a proper binary operation, `a·e = e·a ∈ S` must be satisfied to be a monoid.

``````Identity: a ∈ S, a·e = e·a = a ∈ S
``````

Some documentation calls this using the number 1 and the any alphabet in subscript — for example, 1x referring to the identity on the variable x. Or some documentation uses just a single alphabet letter, such as or e.

That’s all there is to know about monoids, let’s practice with some simple examples.

#typescript #programming #functional-programming #javascript #coding #function

1653475560

## A Pure PHP Implementation Of The MessagePack Serialization Format

msgpack.php

A pure PHP implementation of the MessagePack serialization format.

## Installation

The recommended way to install the library is through Composer:

``````composer require rybakit/msgpack
``````

## Usage

### Packing

To pack values you can either use an instance of a `Packer`:

``````\$packer = new Packer();
\$packed = \$packer->pack(\$value);
``````

or call a static method on the `MessagePack` class:

``````\$packed = MessagePack::pack(\$value);
``````

In the examples above, the method `pack` automatically packs a value depending on its type. However, not all PHP types can be uniquely translated to MessagePack types. For example, the MessagePack format defines `map` and `array` types, which are represented by a single `array` type in PHP. By default, the packer will pack a PHP array as a MessagePack array if it has sequential numeric keys, starting from `0` and as a MessagePack map otherwise:

``````\$mpArr1 = \$packer->pack([1, 2]);               // MP array [1, 2]
\$mpArr2 = \$packer->pack([0 => 1, 1 => 2]);     // MP array [1, 2]
\$mpMap1 = \$packer->pack([0 => 1, 2 => 3]);     // MP map {0: 1, 2: 3}
\$mpMap2 = \$packer->pack([1 => 2, 2 => 3]);     // MP map {1: 2, 2: 3}
\$mpMap3 = \$packer->pack(['a' => 1, 'b' => 2]); // MP map {a: 1, b: 2}
``````

However, sometimes you need to pack a sequential array as a MessagePack map. To do this, use the `packMap` method:

``````\$mpMap = \$packer->packMap([1, 2]); // {0: 1, 1: 2}
``````

Here is a list of type-specific packing methods:

``````\$packer->packNil();           // MP nil
\$packer->packBool(true);      // MP bool
\$packer->packInt(42);         // MP int
\$packer->packFloat(M_PI);     // MP float (32 or 64)
\$packer->packFloat32(M_PI);   // MP float 32
\$packer->packFloat64(M_PI);   // MP float 64
\$packer->packStr('foo');      // MP str
\$packer->packBin("\x80");     // MP bin
\$packer->packArray([1, 2]);   // MP array
\$packer->packMap(['a' => 1]); // MP map
\$packer->packExt(1, "\xaa");  // MP ext
``````

Check the "Custom types" section below on how to pack custom types.

#### Packing options

The `Packer` object supports a number of bitmask-based options for fine-tuning the packing process (defaults are in bold):

The type detection mode (`DETECT_STR_BIN`/`DETECT_ARR_MAP`) adds some overhead which can be noticed when you pack large (16- and 32-bit) arrays or strings. However, if you know the value type in advance (for example, you only work with UTF-8 strings or/and associative arrays), you can eliminate this overhead by forcing the packer to use the appropriate type, which will save it from running the auto-detection routine. Another option is to explicitly specify the value type. The library provides 2 auxiliary classes for this, `Map` and `Bin`. Check the "Custom types" section below for details.

Examples:

``````// detect str/bin type and pack PHP 64-bit floats (doubles) to MP 32-bit floats
\$packer = new Packer(PackOptions::DETECT_STR_BIN | PackOptions::FORCE_FLOAT32);

// these will throw MessagePack\Exception\InvalidOptionException
\$packer = new Packer(PackOptions::FORCE_STR | PackOptions::FORCE_BIN);
\$packer = new Packer(PackOptions::FORCE_FLOAT32 | PackOptions::FORCE_FLOAT64);
``````

### Unpacking

To unpack data you can either use an instance of a `BufferUnpacker`:

``````\$unpacker = new BufferUnpacker();

\$unpacker->reset(\$packed);
\$value = \$unpacker->unpack();
``````

or call a static method on the `MessagePack` class:

``````\$value = MessagePack::unpack(\$packed);
``````

If the packed data is received in chunks (e.g. when reading from a stream), use the `tryUnpack` method, which attempts to unpack data and returns an array of unpacked messages (if any) instead of throwing an `InsufficientDataException`:

``````while (\$chunk = ...) {
\$unpacker->append(\$chunk);
if (\$messages = \$unpacker->tryUnpack()) {
return \$messages;
}
}
``````

If you want to unpack from a specific position in a buffer, use `seek`:

``````\$unpacker->seek(42); // set position equal to 42 bytes
\$unpacker->seek(-8); // set position to 8 bytes before the end of the buffer
``````

To skip bytes from the current position, use `skip`:

``````\$unpacker->skip(10); // set position to 10 bytes ahead of the current position
``````

To get the number of remaining (unread) bytes in the buffer:

``````\$unreadBytesCount = \$unpacker->getRemainingCount();
``````

To check whether the buffer has unread data:

``````\$hasUnreadBytes = \$unpacker->hasRemaining();
``````

If needed, you can remove already read data from the buffer by calling:

``````\$releasedBytesCount = \$unpacker->release();
``````

With the `read` method you can read raw (packed) data:

``````\$packedData = \$unpacker->read(2); // read 2 bytes
``````

Besides the above methods `BufferUnpacker` provides type-specific unpacking methods, namely:

``````\$unpacker->unpackNil();   // PHP null
\$unpacker->unpackBool();  // PHP bool
\$unpacker->unpackInt();   // PHP int
\$unpacker->unpackFloat(); // PHP float
\$unpacker->unpackStr();   // PHP UTF-8 string
\$unpacker->unpackBin();   // PHP binary string
\$unpacker->unpackArray(); // PHP sequential array
\$unpacker->unpackMap();   // PHP associative array
\$unpacker->unpackExt();   // PHP MessagePack\Type\Ext object
``````

#### Unpacking options

The `BufferUnpacker` object supports a number of bitmask-based options for fine-tuning the unpacking process (defaults are in bold):

1. The binary MessagePack format has unsigned 64-bit as its largest integer data type, but PHP does not support such integers, which means that an overflow can occur during unpacking.

2. Make sure the GMP extension is enabled.

3. Make sure the Decimal extension is enabled.

Examples:

``````\$packedUint64 = "\xcf"."\xff\xff\xff\xff"."\xff\xff\xff\xff";

\$unpacker = new BufferUnpacker(\$packedUint64);
var_dump(\$unpacker->unpack()); // string(20) "18446744073709551615"

\$unpacker = new BufferUnpacker(\$packedUint64, UnpackOptions::BIGINT_AS_GMP);
var_dump(\$unpacker->unpack()); // object(GMP) {...}

\$unpacker = new BufferUnpacker(\$packedUint64, UnpackOptions::BIGINT_AS_DEC);
var_dump(\$unpacker->unpack()); // object(Decimal\Decimal) {...}
``````

### Custom types

In addition to the basic types, the library provides functionality to serialize and deserialize arbitrary types. This can be done in several ways, depending on your use case. Let's take a look at them.

#### Type objects

If you need to serialize an instance of one of your classes into one of the basic MessagePack types, the best way to do this is to implement the CanBePacked interface in the class. A good example of such a class is the `Map` type class that comes with the library. This type is useful when you want to explicitly specify that a given PHP array should be packed as a MessagePack map without triggering an automatic type detection routine:

``````\$packer = new Packer();

\$packedMap = \$packer->pack(new Map([1, 2, 3]));
\$packedArray = \$packer->pack([1, 2, 3]);
``````

More type examples can be found in the src/Type directory.

#### Type transformers

As with type objects, type transformers are only responsible for serializing values. They should be used when you need to serialize a value that does not implement the CanBePacked interface. Examples of such values could be instances of built-in or third-party classes that you don't own, or non-objects such as resources.

A transformer class must implement the CanPack interface. To use a transformer, it must first be registered in the packer. Here is an example of how to serialize PHP streams into the MessagePack `bin` format type using one of the supplied transformers, `StreamTransformer`:

``````\$packer = new Packer(null, [new StreamTransformer()]);

\$packedBin = \$packer->pack(fopen('/path/to/file', 'r+'));
``````

More type transformer examples can be found in the src/TypeTransformer directory.

#### Extensions

In contrast to the cases described above, extensions are intended to handle extension types and are responsible for both serialization and deserialization of values (types).

An extension class must implement the Extension interface. To use an extension, it must first be registered in the packer and the unpacker.

The MessagePack specification divides extension types into two groups: predefined and application-specific. Currently, there is only one predefined type in the specification, Timestamp.

Timestamp

The Timestamp extension type is a predefined type. Support for this type in the library is done through the `TimestampExtension` class. This class is responsible for handling `Timestamp` objects, which represent the number of seconds and optional adjustment in nanoseconds:

``````\$timestampExtension = new TimestampExtension();

\$packer = new Packer();
\$packer = \$packer->extendWith(\$timestampExtension);

\$unpacker = new BufferUnpacker();
\$unpacker = \$unpacker->extendWith(\$timestampExtension);

\$packedTimestamp = \$packer->pack(Timestamp::now());
\$timestamp = \$unpacker->reset(\$packedTimestamp)->unpack();

\$seconds = \$timestamp->getSeconds();
\$nanoseconds = \$timestamp->getNanoseconds();
``````

When using the `MessagePack` class, the Timestamp extension is already registered:

``````\$packedTimestamp = MessagePack::pack(Timestamp::now());
\$timestamp = MessagePack::unpack(\$packedTimestamp);
``````

Application-specific extensions

In addition, the format can be extended with your own types. For example, to make the built-in PHP `DateTime` objects first-class citizens in your code, you can create a corresponding extension, as shown in the example. Please note, that custom extensions have to be registered with a unique extension ID (an integer from `0` to `127`).

More extension examples can be found in the examples/MessagePack directory.

## Exceptions

If an error occurs during packing/unpacking, a `PackingFailedException` or an `UnpackingFailedException` will be thrown, respectively. In addition, an `InsufficientDataException` can be thrown during unpacking.

An `InvalidOptionException` will be thrown in case an invalid option (or a combination of mutually exclusive options) is used.

## Tests

Run tests as follows:

``````vendor/bin/phpunit
``````

Also, if you already have Docker installed, you can run the tests in a docker container. First, create a container:

``````./dockerfile.sh | docker build -t msgpack -
``````

The command above will create a container named `msgpack` with PHP 8.1 runtime. You may change the default runtime by defining the `PHP_IMAGE` environment variable:

``````PHP_IMAGE='php:8.0-cli' ./dockerfile.sh | docker build -t msgpack -
``````

See a list of various images here.

Then run the unit tests:

``````docker run --rm -v \$PWD:/msgpack -w /msgpack msgpack
``````

#### Fuzzing

To ensure that the unpacking works correctly with malformed/semi-malformed data, you can use a testing technique called Fuzzing. The library ships with a help file (target) for PHP-Fuzzer and can be used as follows:

``````php-fuzzer fuzz tests/fuzz_buffer_unpacker.php
``````

#### Performance

To check performance, run:

``````php -n -dzend_extension=opcache.so \
-dpcre.jit=1 -dopcache.enable=1 -dopcache.enable_cli=1 \
tests/bench.php
``````

Example output

``````Filter: MessagePack\Tests\Perf\Filter\ListFilter
Rounds: 3
Iterations: 100000

=============================================
Test/Target            Packer  BufferUnpacker
---------------------------------------------
nil .................. 0.0030 ........ 0.0139
false ................ 0.0037 ........ 0.0144
true ................. 0.0040 ........ 0.0137
7-bit uint #1 ........ 0.0052 ........ 0.0120
7-bit uint #2 ........ 0.0059 ........ 0.0114
7-bit uint #3 ........ 0.0061 ........ 0.0119
5-bit sint #1 ........ 0.0067 ........ 0.0126
5-bit sint #2 ........ 0.0064 ........ 0.0132
5-bit sint #3 ........ 0.0066 ........ 0.0135
8-bit uint #1 ........ 0.0078 ........ 0.0200
8-bit uint #2 ........ 0.0077 ........ 0.0212
8-bit uint #3 ........ 0.0086 ........ 0.0203
16-bit uint #1 ....... 0.0111 ........ 0.0271
16-bit uint #2 ....... 0.0115 ........ 0.0260
16-bit uint #3 ....... 0.0103 ........ 0.0273
32-bit uint #1 ....... 0.0116 ........ 0.0326
32-bit uint #2 ....... 0.0118 ........ 0.0332
32-bit uint #3 ....... 0.0127 ........ 0.0325
64-bit uint #1 ....... 0.0140 ........ 0.0277
64-bit uint #2 ....... 0.0134 ........ 0.0294
64-bit uint #3 ....... 0.0134 ........ 0.0281
8-bit int #1 ......... 0.0086 ........ 0.0241
8-bit int #2 ......... 0.0089 ........ 0.0225
8-bit int #3 ......... 0.0085 ........ 0.0229
16-bit int #1 ........ 0.0118 ........ 0.0280
16-bit int #2 ........ 0.0121 ........ 0.0270
16-bit int #3 ........ 0.0109 ........ 0.0274
32-bit int #1 ........ 0.0128 ........ 0.0346
32-bit int #2 ........ 0.0118 ........ 0.0339
32-bit int #3 ........ 0.0135 ........ 0.0368
64-bit int #1 ........ 0.0138 ........ 0.0276
64-bit int #2 ........ 0.0132 ........ 0.0286
64-bit int #3 ........ 0.0137 ........ 0.0274
64-bit int #4 ........ 0.0180 ........ 0.0285
64-bit float #1 ...... 0.0134 ........ 0.0284
64-bit float #2 ...... 0.0125 ........ 0.0275
64-bit float #3 ...... 0.0126 ........ 0.0283
fix string #1 ........ 0.0035 ........ 0.0133
fix string #2 ........ 0.0094 ........ 0.0216
fix string #3 ........ 0.0094 ........ 0.0222
fix string #4 ........ 0.0091 ........ 0.0241
8-bit string #1 ...... 0.0122 ........ 0.0301
8-bit string #2 ...... 0.0118 ........ 0.0304
8-bit string #3 ...... 0.0119 ........ 0.0315
16-bit string #1 ..... 0.0150 ........ 0.0388
16-bit string #2 ..... 0.1545 ........ 0.1665
32-bit string ........ 0.1570 ........ 0.1756
wide char string #1 .. 0.0091 ........ 0.0236
wide char string #2 .. 0.0122 ........ 0.0313
8-bit binary #1 ...... 0.0100 ........ 0.0302
8-bit binary #2 ...... 0.0123 ........ 0.0324
8-bit binary #3 ...... 0.0126 ........ 0.0327
16-bit binary ........ 0.0168 ........ 0.0372
32-bit binary ........ 0.1588 ........ 0.1754
fix array #1 ......... 0.0042 ........ 0.0131
fix array #2 ......... 0.0294 ........ 0.0367
fix array #3 ......... 0.0412 ........ 0.0472
16-bit array #1 ...... 0.1378 ........ 0.1596
16-bit array #2 ........... S ............. S
32-bit array .............. S ............. S
complex array ........ 0.1865 ........ 0.2283
fix map #1 ........... 0.0725 ........ 0.1048
fix map #2 ........... 0.0319 ........ 0.0405
fix map #3 ........... 0.0356 ........ 0.0665
fix map #4 ........... 0.0465 ........ 0.0497
16-bit map #1 ........ 0.2540 ........ 0.3028
16-bit map #2 ............. S ............. S
32-bit map ................ S ............. S
complex map .......... 0.2372 ........ 0.2710
fixext 1 ............. 0.0283 ........ 0.0358
fixext 2 ............. 0.0291 ........ 0.0371
fixext 4 ............. 0.0302 ........ 0.0355
fixext 8 ............. 0.0288 ........ 0.0384
fixext 16 ............ 0.0293 ........ 0.0359
8-bit ext ............ 0.0302 ........ 0.0439
16-bit ext ........... 0.0334 ........ 0.0499
32-bit ext ........... 0.1845 ........ 0.1888
32-bit timestamp #1 .. 0.0337 ........ 0.0547
32-bit timestamp #2 .. 0.0335 ........ 0.0560
64-bit timestamp #1 .. 0.0371 ........ 0.0575
64-bit timestamp #2 .. 0.0374 ........ 0.0542
64-bit timestamp #3 .. 0.0356 ........ 0.0533
96-bit timestamp #1 .. 0.0362 ........ 0.0699
96-bit timestamp #2 .. 0.0381 ........ 0.0701
96-bit timestamp #3 .. 0.0367 ........ 0.0687
=============================================
Total                  2.7618          4.0820
Skipped                     4               4
Failed                      0               0
Ignored                     0               0
``````

With JIT:

``````php -n -dzend_extension=opcache.so \
-dpcre.jit=1 -dopcache.jit_buffer_size=64M -dopcache.jit=tracing -dopcache.enable=1 -dopcache.enable_cli=1 \
tests/bench.php
``````

Example output

``````Filter: MessagePack\Tests\Perf\Filter\ListFilter
Rounds: 3
Iterations: 100000

=============================================
Test/Target            Packer  BufferUnpacker
---------------------------------------------
nil .................. 0.0005 ........ 0.0054
false ................ 0.0004 ........ 0.0059
true ................. 0.0004 ........ 0.0059
7-bit uint #1 ........ 0.0010 ........ 0.0047
7-bit uint #2 ........ 0.0010 ........ 0.0046
7-bit uint #3 ........ 0.0010 ........ 0.0046
5-bit sint #1 ........ 0.0025 ........ 0.0046
5-bit sint #2 ........ 0.0023 ........ 0.0046
5-bit sint #3 ........ 0.0024 ........ 0.0045
8-bit uint #1 ........ 0.0043 ........ 0.0081
8-bit uint #2 ........ 0.0043 ........ 0.0079
8-bit uint #3 ........ 0.0041 ........ 0.0080
16-bit uint #1 ....... 0.0064 ........ 0.0095
16-bit uint #2 ....... 0.0064 ........ 0.0091
16-bit uint #3 ....... 0.0064 ........ 0.0094
32-bit uint #1 ....... 0.0085 ........ 0.0114
32-bit uint #2 ....... 0.0077 ........ 0.0122
32-bit uint #3 ....... 0.0077 ........ 0.0120
64-bit uint #1 ....... 0.0085 ........ 0.0159
64-bit uint #2 ....... 0.0086 ........ 0.0157
64-bit uint #3 ....... 0.0086 ........ 0.0158
8-bit int #1 ......... 0.0042 ........ 0.0080
8-bit int #2 ......... 0.0042 ........ 0.0080
8-bit int #3 ......... 0.0042 ........ 0.0081
16-bit int #1 ........ 0.0065 ........ 0.0095
16-bit int #2 ........ 0.0065 ........ 0.0090
16-bit int #3 ........ 0.0056 ........ 0.0085
32-bit int #1 ........ 0.0067 ........ 0.0107
32-bit int #2 ........ 0.0066 ........ 0.0106
32-bit int #3 ........ 0.0063 ........ 0.0104
64-bit int #1 ........ 0.0072 ........ 0.0162
64-bit int #2 ........ 0.0073 ........ 0.0174
64-bit int #3 ........ 0.0072 ........ 0.0164
64-bit int #4 ........ 0.0077 ........ 0.0161
64-bit float #1 ...... 0.0053 ........ 0.0135
64-bit float #2 ...... 0.0053 ........ 0.0135
64-bit float #3 ...... 0.0052 ........ 0.0135
fix string #1 ....... -0.0002 ........ 0.0044
fix string #2 ........ 0.0035 ........ 0.0067
fix string #3 ........ 0.0035 ........ 0.0077
fix string #4 ........ 0.0033 ........ 0.0078
8-bit string #1 ...... 0.0059 ........ 0.0110
8-bit string #2 ...... 0.0063 ........ 0.0121
8-bit string #3 ...... 0.0064 ........ 0.0124
16-bit string #1 ..... 0.0099 ........ 0.0146
16-bit string #2 ..... 0.1522 ........ 0.1474
32-bit string ........ 0.1511 ........ 0.1483
wide char string #1 .. 0.0039 ........ 0.0084
wide char string #2 .. 0.0073 ........ 0.0123
8-bit binary #1 ...... 0.0040 ........ 0.0112
8-bit binary #2 ...... 0.0075 ........ 0.0123
8-bit binary #3 ...... 0.0077 ........ 0.0129
16-bit binary ........ 0.0096 ........ 0.0145
32-bit binary ........ 0.1535 ........ 0.1479
fix array #1 ......... 0.0008 ........ 0.0061
fix array #2 ......... 0.0121 ........ 0.0165
fix array #3 ......... 0.0193 ........ 0.0222
16-bit array #1 ...... 0.0607 ........ 0.0479
16-bit array #2 ........... S ............. S
32-bit array .............. S ............. S
complex array ........ 0.0749 ........ 0.0824
fix map #1 ........... 0.0329 ........ 0.0431
fix map #2 ........... 0.0161 ........ 0.0189
fix map #3 ........... 0.0205 ........ 0.0262
fix map #4 ........... 0.0252 ........ 0.0205
16-bit map #1 ........ 0.1016 ........ 0.0927
16-bit map #2 ............. S ............. S
32-bit map ................ S ............. S
complex map .......... 0.1096 ........ 0.1030
fixext 1 ............. 0.0157 ........ 0.0161
fixext 2 ............. 0.0175 ........ 0.0183
fixext 4 ............. 0.0156 ........ 0.0185
fixext 8 ............. 0.0163 ........ 0.0184
fixext 16 ............ 0.0164 ........ 0.0182
8-bit ext ............ 0.0158 ........ 0.0207
16-bit ext ........... 0.0203 ........ 0.0219
32-bit ext ........... 0.1614 ........ 0.1539
32-bit timestamp #1 .. 0.0195 ........ 0.0249
32-bit timestamp #2 .. 0.0188 ........ 0.0260
64-bit timestamp #1 .. 0.0207 ........ 0.0281
64-bit timestamp #2 .. 0.0212 ........ 0.0291
64-bit timestamp #3 .. 0.0207 ........ 0.0295
96-bit timestamp #1 .. 0.0222 ........ 0.0358
96-bit timestamp #2 .. 0.0228 ........ 0.0353
96-bit timestamp #3 .. 0.0210 ........ 0.0319
=============================================
Total                  1.6432          1.9674
Skipped                     4               4
Failed                      0               0
Ignored                     0               0
``````

You may change default benchmark settings by defining the following environment variables:

For example:

``````export MP_BENCH_TARGETS=pure_p
export MP_BENCH_ITERATIONS=1000000
export MP_BENCH_ROUNDS=5
# a comma separated list of test names
export MP_BENCH_TESTS='complex array, complex map'
# or a group name
# export MP_BENCH_TESTS='-@slow' // @pecl_comp
# or a regexp
# export MP_BENCH_TESTS='/complex (array|map)/'
``````

Another example, benchmarking both the library and the PECL extension:

``````MP_BENCH_TARGETS=pure_p,pure_u,pecl_p,pecl_u \
php -n -dextension=msgpack.so -dzend_extension=opcache.so \
-dpcre.jit=1 -dopcache.enable=1 -dopcache.enable_cli=1 \
tests/bench.php
``````

Example output

``````Filter: MessagePack\Tests\Perf\Filter\ListFilter
Rounds: 3
Iterations: 100000

===========================================================================
Test/Target            Packer  BufferUnpacker  msgpack_pack  msgpack_unpack
---------------------------------------------------------------------------
nil .................. 0.0031 ........ 0.0141 ...... 0.0055 ........ 0.0064
false ................ 0.0039 ........ 0.0154 ...... 0.0056 ........ 0.0053
true ................. 0.0038 ........ 0.0139 ...... 0.0056 ........ 0.0044
7-bit uint #1 ........ 0.0061 ........ 0.0110 ...... 0.0059 ........ 0.0046
7-bit uint #2 ........ 0.0065 ........ 0.0119 ...... 0.0042 ........ 0.0029
7-bit uint #3 ........ 0.0054 ........ 0.0117 ...... 0.0045 ........ 0.0025
5-bit sint #1 ........ 0.0047 ........ 0.0103 ...... 0.0038 ........ 0.0022
5-bit sint #2 ........ 0.0048 ........ 0.0117 ...... 0.0038 ........ 0.0022
5-bit sint #3 ........ 0.0046 ........ 0.0102 ...... 0.0038 ........ 0.0023
8-bit uint #1 ........ 0.0063 ........ 0.0174 ...... 0.0039 ........ 0.0031
8-bit uint #2 ........ 0.0063 ........ 0.0167 ...... 0.0040 ........ 0.0029
8-bit uint #3 ........ 0.0063 ........ 0.0168 ...... 0.0039 ........ 0.0030
16-bit uint #1 ....... 0.0092 ........ 0.0222 ...... 0.0049 ........ 0.0030
16-bit uint #2 ....... 0.0096 ........ 0.0227 ...... 0.0042 ........ 0.0046
16-bit uint #3 ....... 0.0123 ........ 0.0274 ...... 0.0059 ........ 0.0051
32-bit uint #1 ....... 0.0136 ........ 0.0331 ...... 0.0060 ........ 0.0048
32-bit uint #2 ....... 0.0130 ........ 0.0336 ...... 0.0070 ........ 0.0048
32-bit uint #3 ....... 0.0127 ........ 0.0329 ...... 0.0051 ........ 0.0048
64-bit uint #1 ....... 0.0126 ........ 0.0268 ...... 0.0055 ........ 0.0049
64-bit uint #2 ....... 0.0135 ........ 0.0281 ...... 0.0052 ........ 0.0046
64-bit uint #3 ....... 0.0131 ........ 0.0274 ...... 0.0069 ........ 0.0044
8-bit int #1 ......... 0.0077 ........ 0.0236 ...... 0.0058 ........ 0.0044
8-bit int #2 ......... 0.0087 ........ 0.0244 ...... 0.0058 ........ 0.0048
8-bit int #3 ......... 0.0084 ........ 0.0241 ...... 0.0055 ........ 0.0049
16-bit int #1 ........ 0.0112 ........ 0.0271 ...... 0.0048 ........ 0.0045
16-bit int #2 ........ 0.0124 ........ 0.0292 ...... 0.0057 ........ 0.0049
16-bit int #3 ........ 0.0118 ........ 0.0270 ...... 0.0058 ........ 0.0050
32-bit int #1 ........ 0.0137 ........ 0.0366 ...... 0.0058 ........ 0.0051
32-bit int #2 ........ 0.0133 ........ 0.0366 ...... 0.0056 ........ 0.0049
32-bit int #3 ........ 0.0129 ........ 0.0350 ...... 0.0052 ........ 0.0048
64-bit int #1 ........ 0.0145 ........ 0.0254 ...... 0.0034 ........ 0.0025
64-bit int #2 ........ 0.0097 ........ 0.0214 ...... 0.0034 ........ 0.0025
64-bit int #3 ........ 0.0096 ........ 0.0287 ...... 0.0059 ........ 0.0050
64-bit int #4 ........ 0.0143 ........ 0.0277 ...... 0.0059 ........ 0.0046
64-bit float #1 ...... 0.0134 ........ 0.0281 ...... 0.0057 ........ 0.0052
64-bit float #2 ...... 0.0141 ........ 0.0281 ...... 0.0057 ........ 0.0050
64-bit float #3 ...... 0.0144 ........ 0.0282 ...... 0.0057 ........ 0.0050
fix string #1 ........ 0.0036 ........ 0.0143 ...... 0.0066 ........ 0.0053
fix string #2 ........ 0.0107 ........ 0.0222 ...... 0.0065 ........ 0.0068
fix string #3 ........ 0.0116 ........ 0.0245 ...... 0.0063 ........ 0.0069
fix string #4 ........ 0.0105 ........ 0.0253 ...... 0.0083 ........ 0.0077
8-bit string #1 ...... 0.0126 ........ 0.0318 ...... 0.0075 ........ 0.0088
8-bit string #2 ...... 0.0121 ........ 0.0295 ...... 0.0076 ........ 0.0086
8-bit string #3 ...... 0.0125 ........ 0.0293 ...... 0.0130 ........ 0.0093
16-bit string #1 ..... 0.0159 ........ 0.0368 ...... 0.0117 ........ 0.0086
16-bit string #2 ..... 0.1547 ........ 0.1686 ...... 0.1516 ........ 0.1373
32-bit string ........ 0.1558 ........ 0.1729 ...... 0.1511 ........ 0.1396
wide char string #1 .. 0.0098 ........ 0.0237 ...... 0.0066 ........ 0.0065
wide char string #2 .. 0.0128 ........ 0.0291 ...... 0.0061 ........ 0.0082
8-bit binary #1 ........... I ............. I ........... F ............. I
8-bit binary #2 ........... I ............. I ........... F ............. I
8-bit binary #3 ........... I ............. I ........... F ............. I
16-bit binary ............. I ............. I ........... F ............. I
32-bit binary ............. I ............. I ........... F ............. I
fix array #1 ......... 0.0040 ........ 0.0129 ...... 0.0120 ........ 0.0058
fix array #2 ......... 0.0279 ........ 0.0390 ...... 0.0143 ........ 0.0165
fix array #3 ......... 0.0415 ........ 0.0463 ...... 0.0162 ........ 0.0187
16-bit array #1 ...... 0.1349 ........ 0.1628 ...... 0.0334 ........ 0.0341
16-bit array #2 ........... S ............. S ........... S ............. S
32-bit array .............. S ............. S ........... S ............. S
complex array ............. I ............. I ........... F ............. F
fix map #1 ................ I ............. I ........... F ............. I
fix map #2 ........... 0.0345 ........ 0.0391 ...... 0.0143 ........ 0.0168
fix map #3 ................ I ............. I ........... F ............. I
fix map #4 ........... 0.0459 ........ 0.0473 ...... 0.0151 ........ 0.0163
16-bit map #1 ........ 0.2518 ........ 0.2962 ...... 0.0400 ........ 0.0490
16-bit map #2 ............. S ............. S ........... S ............. S
32-bit map ................ S ............. S ........... S ............. S
complex map .......... 0.2380 ........ 0.2682 ...... 0.0545 ........ 0.0579
fixext 1 .................. I ............. I ........... F ............. F
fixext 2 .................. I ............. I ........... F ............. F
fixext 4 .................. I ............. I ........... F ............. F
fixext 8 .................. I ............. I ........... F ............. F
fixext 16 ................. I ............. I ........... F ............. F
8-bit ext ................. I ............. I ........... F ............. F
16-bit ext ................ I ............. I ........... F ............. F
32-bit ext ................ I ............. I ........... F ............. F
32-bit timestamp #1 ....... I ............. I ........... F ............. F
32-bit timestamp #2 ....... I ............. I ........... F ............. F
64-bit timestamp #1 ....... I ............. I ........... F ............. F
64-bit timestamp #2 ....... I ............. I ........... F ............. F
64-bit timestamp #3 ....... I ............. I ........... F ............. F
96-bit timestamp #1 ....... I ............. I ........... F ............. F
96-bit timestamp #2 ....... I ............. I ........... F ............. F
96-bit timestamp #3 ....... I ............. I ........... F ............. F
===========================================================================
Total                  1.5625          2.3866        0.7735          0.7243
Skipped                     4               4             4               4
Failed                      0               0            24              17
Ignored                    24              24             0               7
``````

With JIT:

``````MP_BENCH_TARGETS=pure_p,pure_u,pecl_p,pecl_u \
php -n -dextension=msgpack.so -dzend_extension=opcache.so \
-dpcre.jit=1 -dopcache.jit_buffer_size=64M -dopcache.jit=tracing -dopcache.enable=1 -dopcache.enable_cli=1 \
tests/bench.php
``````

Example output

``````Filter: MessagePack\Tests\Perf\Filter\ListFilter
Rounds: 3
Iterations: 100000

===========================================================================
Test/Target            Packer  BufferUnpacker  msgpack_pack  msgpack_unpack
---------------------------------------------------------------------------
nil .................. 0.0001 ........ 0.0052 ...... 0.0053 ........ 0.0042
false ................ 0.0007 ........ 0.0060 ...... 0.0057 ........ 0.0043
true ................. 0.0008 ........ 0.0060 ...... 0.0056 ........ 0.0041
7-bit uint #1 ........ 0.0031 ........ 0.0046 ...... 0.0062 ........ 0.0041
7-bit uint #2 ........ 0.0021 ........ 0.0043 ...... 0.0062 ........ 0.0041
7-bit uint #3 ........ 0.0022 ........ 0.0044 ...... 0.0061 ........ 0.0040
5-bit sint #1 ........ 0.0030 ........ 0.0048 ...... 0.0062 ........ 0.0040
5-bit sint #2 ........ 0.0032 ........ 0.0046 ...... 0.0062 ........ 0.0040
5-bit sint #3 ........ 0.0031 ........ 0.0046 ...... 0.0062 ........ 0.0040
8-bit uint #1 ........ 0.0054 ........ 0.0079 ...... 0.0062 ........ 0.0050
8-bit uint #2 ........ 0.0051 ........ 0.0079 ...... 0.0064 ........ 0.0044
8-bit uint #3 ........ 0.0051 ........ 0.0082 ...... 0.0062 ........ 0.0044
16-bit uint #1 ....... 0.0077 ........ 0.0094 ...... 0.0065 ........ 0.0045
16-bit uint #2 ....... 0.0077 ........ 0.0094 ...... 0.0063 ........ 0.0045
16-bit uint #3 ....... 0.0077 ........ 0.0095 ...... 0.0064 ........ 0.0047
32-bit uint #1 ....... 0.0088 ........ 0.0119 ...... 0.0063 ........ 0.0043
32-bit uint #2 ....... 0.0089 ........ 0.0117 ...... 0.0062 ........ 0.0039
32-bit uint #3 ....... 0.0089 ........ 0.0118 ...... 0.0063 ........ 0.0044
64-bit uint #1 ....... 0.0097 ........ 0.0155 ...... 0.0063 ........ 0.0045
64-bit uint #2 ....... 0.0095 ........ 0.0153 ...... 0.0061 ........ 0.0045
64-bit uint #3 ....... 0.0096 ........ 0.0156 ...... 0.0063 ........ 0.0047
8-bit int #1 ......... 0.0053 ........ 0.0083 ...... 0.0062 ........ 0.0044
8-bit int #2 ......... 0.0052 ........ 0.0080 ...... 0.0062 ........ 0.0044
8-bit int #3 ......... 0.0052 ........ 0.0080 ...... 0.0062 ........ 0.0043
16-bit int #1 ........ 0.0089 ........ 0.0097 ...... 0.0069 ........ 0.0046
16-bit int #2 ........ 0.0075 ........ 0.0093 ...... 0.0063 ........ 0.0043
16-bit int #3 ........ 0.0075 ........ 0.0094 ...... 0.0062 ........ 0.0046
32-bit int #1 ........ 0.0086 ........ 0.0122 ...... 0.0063 ........ 0.0044
32-bit int #2 ........ 0.0087 ........ 0.0120 ...... 0.0066 ........ 0.0046
32-bit int #3 ........ 0.0086 ........ 0.0121 ...... 0.0060 ........ 0.0044
64-bit int #1 ........ 0.0096 ........ 0.0149 ...... 0.0060 ........ 0.0045
64-bit int #2 ........ 0.0096 ........ 0.0157 ...... 0.0062 ........ 0.0044
64-bit int #3 ........ 0.0096 ........ 0.0160 ...... 0.0063 ........ 0.0046
64-bit int #4 ........ 0.0097 ........ 0.0157 ...... 0.0061 ........ 0.0044
64-bit float #1 ...... 0.0079 ........ 0.0153 ...... 0.0056 ........ 0.0044
64-bit float #2 ...... 0.0079 ........ 0.0152 ...... 0.0057 ........ 0.0045
64-bit float #3 ...... 0.0079 ........ 0.0155 ...... 0.0057 ........ 0.0044
fix string #1 ........ 0.0010 ........ 0.0045 ...... 0.0071 ........ 0.0044
fix string #2 ........ 0.0048 ........ 0.0075 ...... 0.0070 ........ 0.0060
fix string #3 ........ 0.0048 ........ 0.0086 ...... 0.0068 ........ 0.0060
fix string #4 ........ 0.0050 ........ 0.0088 ...... 0.0070 ........ 0.0059
8-bit string #1 ...... 0.0081 ........ 0.0129 ...... 0.0069 ........ 0.0062
8-bit string #2 ...... 0.0086 ........ 0.0128 ...... 0.0069 ........ 0.0065
8-bit string #3 ...... 0.0086 ........ 0.0126 ...... 0.0115 ........ 0.0065
16-bit string #1 ..... 0.0105 ........ 0.0137 ...... 0.0128 ........ 0.0068
16-bit string #2 ..... 0.1510 ........ 0.1486 ...... 0.1526 ........ 0.1391
32-bit string ........ 0.1517 ........ 0.1475 ...... 0.1504 ........ 0.1370
wide char string #1 .. 0.0044 ........ 0.0085 ...... 0.0067 ........ 0.0057
wide char string #2 .. 0.0081 ........ 0.0125 ...... 0.0069 ........ 0.0063
8-bit binary #1 ........... I ............. I ........... F ............. I
8-bit binary #2 ........... I ............. I ........... F ............. I
8-bit binary #3 ........... I ............. I ........... F ............. I
16-bit binary ............. I ............. I ........... F ............. I
32-bit binary ............. I ............. I ........... F ............. I
fix array #1 ......... 0.0014 ........ 0.0059 ...... 0.0132 ........ 0.0055
fix array #2 ......... 0.0146 ........ 0.0156 ...... 0.0155 ........ 0.0148
fix array #3 ......... 0.0211 ........ 0.0229 ...... 0.0179 ........ 0.0180
16-bit array #1 ...... 0.0673 ........ 0.0498 ...... 0.0343 ........ 0.0388
16-bit array #2 ........... S ............. S ........... S ............. S
32-bit array .............. S ............. S ........... S ............. S
complex array ............. I ............. I ........... F ............. F
fix map #1 ................ I ............. I ........... F ............. I
fix map #2 ........... 0.0148 ........ 0.0180 ...... 0.0156 ........ 0.0179
fix map #3 ................ I ............. I ........... F ............. I
fix map #4 ........... 0.0252 ........ 0.0201 ...... 0.0214 ........ 0.0167
16-bit map #1 ........ 0.1027 ........ 0.0836 ...... 0.0388 ........ 0.0510
16-bit map #2 ............. S ............. S ........... S ............. S
32-bit map ................ S ............. S ........... S ............. S
complex map .......... 0.1104 ........ 0.1010 ...... 0.0556 ........ 0.0602
fixext 1 .................. I ............. I ........... F ............. F
fixext 2 .................. I ............. I ........... F ............. F
fixext 4 .................. I ............. I ........... F ............. F
fixext 8 .................. I ............. I ........... F ............. F
fixext 16 ................. I ............. I ........... F ............. F
8-bit ext ................. I ............. I ........... F ............. F
16-bit ext ................ I ............. I ........... F ............. F
32-bit ext ................ I ............. I ........... F ............. F
32-bit timestamp #1 ....... I ............. I ........... F ............. F
32-bit timestamp #2 ....... I ............. I ........... F ............. F
64-bit timestamp #1 ....... I ............. I ........... F ............. F
64-bit timestamp #2 ....... I ............. I ........... F ............. F
64-bit timestamp #3 ....... I ............. I ........... F ............. F
96-bit timestamp #1 ....... I ............. I ........... F ............. F
96-bit timestamp #2 ....... I ............. I ........... F ............. F
96-bit timestamp #3 ....... I ............. I ........... F ............. F
===========================================================================
Total                  0.9642          1.0909        0.8224          0.7213
Skipped                     4               4             4               4
Failed                      0               0            24              17
Ignored                    24              24             0               7
``````

Note that the msgpack extension (v2.1.2) doesn't support ext, bin and UTF-8 str types.

The library is released under the MIT License. See the bundled LICENSE file for details.

Author: rybakit
Source Code: https://github.com/rybakit/msgpack.php

1648900800

## plpgsql_check

I founded this project, because I wanted to publish the code I wrote in the last two years, when I tried to write enhanced checking for PostgreSQL upstream. It was not fully successful - integration into upstream requires some larger plpgsql refactoring - probably it will not be done in next years (now is Dec 2013). But written code is fully functional and can be used in production (and it is used in production). So, I created this extension to be available for all plpgsql developers.

If you like it and if you would to join to development of this extension, register yourself to postgresql extension hacking google group.

Features

• check fields of referenced database objects and types inside embedded SQL
• using correct types of function parameters
• unused variables and function argumens, unmodified OUT argumens
• partially detection of dead code (due RETURN command)
• detection of missing RETURN command in function
• try to identify unwanted hidden casts, that can be performance issue like unused indexes
• possibility to collect relations and functions used by function
• possibility to check EXECUTE stmt agaist SQL injection vulnerability

I invite any ideas, patches, bugreports.

plpgsql_check is next generation of plpgsql_lint. It allows to check source code by explicit call plpgsql_check_function.

PostgreSQL PostgreSQL 10, 11, 12, 13 and 14 are supported.

The SQL statements inside PL/pgSQL functions are checked by validator for semantic errors. These errors can be found by plpgsql_check_function:

Active mode

``````postgres=# CREATE EXTENSION plpgsql_check;
postgres=# CREATE TABLE t1(a int, b int);
CREATE TABLE

postgres=#
CREATE OR REPLACE FUNCTION public.f1()
RETURNS void
LANGUAGE plpgsql
AS \$function\$
DECLARE r record;
BEGIN
FOR r IN SELECT * FROM t1
LOOP
RAISE NOTICE '%', r.c; -- there is bug - table t1 missing "c" column
END LOOP;
END;
\$function\$;

CREATE FUNCTION

postgres=# select f1(); -- execution doesn't find a bug due to empty table t1
f1
────

(1 row)

postgres=# \x
Expanded display is on.
postgres=# select * from plpgsql_check_function_tb('f1()');
─[ RECORD 1 ]───────────────────────────
functionid │ f1
lineno     │ 6
statement  │ RAISE
sqlstate   │ 42703
message    │ record "r" has no field "c"
detail     │ [null]
hint       │ [null]
level      │ error
position   │ 0
query      │ [null]

postgres=# \sf+ f1
CREATE OR REPLACE FUNCTION public.f1()
RETURNS void
LANGUAGE plpgsql
1       AS \$function\$
2       DECLARE r record;
3       BEGIN
4         FOR r IN SELECT * FROM t1
5         LOOP
6           RAISE NOTICE '%', r.c; -- there is bug - table t1 missing "c" column
7         END LOOP;
8       END;
9       \$function\$
``````

Function plpgsql_check_function() has three possible formats: text, json or xml

``````select * from plpgsql_check_function('f1()', fatal_errors := false);
plpgsql_check_function
------------------------------------------------------------------------
error:42703:4:SQL statement:column "c" of relation "t1" does not exist
Query: update t1 set c = 30
--                   ^
error:42P01:7:RAISE:missing FROM-clause entry for table "r"
Query: SELECT r.c
--            ^
error:42601:7:RAISE:too few parameters specified for RAISE
(7 rows)

postgres=# select * from plpgsql_check_function('fx()', format:='xml');
plpgsql_check_function
────────────────────────────────────────────────────────────────
<Function oid="16400">                                        ↵
<Issue>                                                     ↵
<Level>error</level>                                      ↵
<Sqlstate>42P01</Sqlstate>                                ↵
<Message>relation "foo111" does not exist</Message>       ↵
<Stmt lineno="3">RETURN</Stmt>                            ↵
<Query position="23">SELECT (select a from foo111)</Query>↵
</Issue>                                                    ↵
</Function>
(1 row)
``````

## Arguments

You can set level of warnings via function's parameters:

### Mandatory arguments

• function name or function signature - these functions requires function specification. Any function in PostgreSQL can be specified by Oid or by name or by signature. When you know oid or complete function's signature, you can use a regprocedure type parameter like `'fx()'::regprocedure` or `16799::regprocedure`. Possible alternative is using a name only, when function's name is unique - like `'fx'`. When the name is not unique or the function doesn't exists it raises a error.

### Optional arguments

`relid DEFAULT 0` - oid of relation assigned with trigger function. It is necessary for check of any trigger function.

`fatal_errors boolean DEFAULT true` - stop on first error

`other_warnings boolean DEFAULT true` - show warnings like different attributes number in assignmenet on left and right side, variable overlaps function's parameter, unused variables, unwanted casting, ..

`extra_warnings boolean DEFAULT true` - show warnings like missing `RETURN`, shadowed variables, dead code, never read (unused) function's parameter, unmodified variables, modified auto variables, ..

`performance_warnings boolean DEFAULT false` - performance related warnings like declared type with type modificator, casting, implicit casts in where clause (can be reason why index is not used), ..

`security_warnings boolean DEFAULT false` - security related checks like SQL injection vulnerability detection

`anyelementtype regtype DEFAULT 'int'` - a real type used instead anyelement type

`anyenumtype regtype DEFAULT '-'` - a real type used instead anyenum type

`anyrangetype regtype DEFAULT 'int4range'` - a real type used instead anyrange type

`anycompatibletype DEFAULT 'int'` - a real type used instead anycompatible type

`anycompatiblerangetype DEFAULT 'int4range'` - a real type used instead anycompatible range type

`without_warnings DEFAULT false` - disable all warnings

`all_warnings DEFAULT false` - enable all warnings

`newtable DEFAULT NULL`, `oldtable DEFAULT NULL` - the names of NEW or OLD transitive tables. These parameters are required when transitive tables are used.

## Triggers

When you want to check any trigger, you have to enter a relation that will be used together with trigger function

``````CREATE TABLE bar(a int, b int);

postgres=# \sf+ foo_trg
CREATE OR REPLACE FUNCTION public.foo_trg()
RETURNS trigger
LANGUAGE plpgsql
1       AS \$function\$
2       BEGIN
3         NEW.c := NEW.a + NEW.b;
4         RETURN NEW;
5       END;
6       \$function\$
``````

Missing relation specification

``````postgres=# select * from plpgsql_check_function('foo_trg()');
ERROR:  missing trigger relation
HINT:  Trigger relation oid must be valid
``````

Correct trigger checking (with specified relation)

``````postgres=# select * from plpgsql_check_function('foo_trg()', 'bar');
plpgsql_check_function
--------------------------------------------------------
error:42703:3:assignment:record "new" has no field "c"
(1 row)
``````

For triggers with transitive tables you can set a `oldtable` or `newtable` parameters:

``````create or replace function footab_trig_func()
returns trigger as \$\$
declare x int;
begin
if false then
-- should be ok;
select count(*) from newtab into x;

-- should fail;
select count(*) from newtab where d = 10 into x;
end if;
return null;
end;
\$\$ language plpgsql;

select * from plpgsql_check_function('footab_trig_func','footab', newtable := 'newtab');
``````

## Mass check

You can use the plpgsql_check_function for mass check functions and mass check triggers. Please, test following queries:

``````-- check all nontrigger plpgsql functions
SELECT p.oid, p.proname, plpgsql_check_function(p.oid)
FROM pg_catalog.pg_namespace n
JOIN pg_catalog.pg_proc p ON pronamespace = n.oid
JOIN pg_catalog.pg_language l ON p.prolang = l.oid
WHERE l.lanname = 'plpgsql' AND p.prorettype <> 2279;
``````

or

``````SELECT p.proname, tgrelid::regclass, cf.*
FROM pg_proc p
JOIN pg_trigger t ON t.tgfoid = p.oid
JOIN pg_language l ON p.prolang = l.oid
JOIN pg_namespace n ON p.pronamespace = n.oid,
LATERAL plpgsql_check_function(p.oid, t.tgrelid) cf
WHERE n.nspname = 'public' and l.lanname = 'plpgsql'
``````

or

``````-- check all plpgsql functions (functions or trigger functions with defined triggers)
SELECT
(pcf).functionid::regprocedure, (pcf).lineno, (pcf).statement,
(pcf).sqlstate, (pcf).message, (pcf).detail, (pcf).hint, (pcf).level,
(pcf)."position", (pcf).query, (pcf).context
FROM
(
SELECT
plpgsql_check_function_tb(pg_proc.oid, COALESCE(pg_trigger.tgrelid, 0)) AS pcf
FROM pg_proc
LEFT JOIN pg_trigger
ON (pg_trigger.tgfoid = pg_proc.oid)
WHERE
prolang = (SELECT lang.oid FROM pg_language lang WHERE lang.lanname = 'plpgsql') AND
pronamespace <> (SELECT nsp.oid FROM pg_namespace nsp WHERE nsp.nspname = 'pg_catalog') AND
-- ignore unused triggers
(pg_proc.prorettype <> (SELECT typ.oid FROM pg_type typ WHERE typ.typname = 'trigger') OR
pg_trigger.tgfoid IS NOT NULL)
OFFSET 0
) ss
ORDER BY (pcf).functionid::regprocedure::text, (pcf).lineno
``````

Passive mode

Functions should be checked on start - plpgsql_check module must be loaded.

## Configuration

``````plpgsql_check.mode = [ disabled | by_function | fresh_start | every_start ]
plpgsql_check.fatal_errors = [ yes | no ]

plpgsql_check.show_nonperformance_warnings = false
plpgsql_check.show_performance_warnings = false
``````

Default mode is by_function, that means that the enhanced check is done only in active mode - by plpgsql_check_function. `fresh_start` means cold start.

You can enable passive mode by

``````load 'plpgsql'; -- 1.1 and higher doesn't need it
set plpgsql_check.mode = 'every_start';

SELECT fx(10); -- run functions - function is checked before runtime starts it
``````

Limits

plpgsql_check should find almost all errors on really static code. When developer use some PLpgSQL's dynamic features like dynamic SQL or record data type, then false positives are possible. These should be rare - in well written code - and then the affected function should be redesigned or plpgsql_check should be disabled for this function.

``````CREATE OR REPLACE FUNCTION f1()
RETURNS void AS \$\$
DECLARE r record;
BEGIN
FOR r IN EXECUTE 'SELECT * FROM t1'
LOOP
RAISE NOTICE '%', r.c;
END LOOP;
END;
\$\$ LANGUAGE plpgsql SET plpgsql.enable_check TO false;
``````

A usage of plpgsql_check adds a small overhead (in enabled passive mode) and you should use it only in develop or preprod environments.

## Dynamic SQL

This module doesn't check queries that are assembled in runtime. It is not possible to identify results of dynamic queries - so plpgsql_check cannot to set correct type to record variables and cannot to check a dependent SQLs and expressions.

When type of record's variable is not know, you can assign it explicitly with pragma `type`:

``````DECLARE r record;
BEGIN
EXECUTE format('SELECT * FROM %I', _tablename) INTO r;
PERFORM plpgsql_check_pragma('type: r (id int, processed bool)');
IF NOT r.processed THEN
...
``````

Attention: The SQL injection check can detect only some SQL injection vulnerabilities. This tool cannot be used for security audit! Some issues should not be detected. This check can raise false alarms too - probably when variable is sanitized by other command or when value is of some compose type.

## Refcursors

plpgsql_check should not to detect structure of referenced cursors. A reference on cursor in PLpgSQL is implemented as name of global cursor. In check time, the name is not known (not in all possibilities), and global cursor doesn't exist. It is significant break for any static analyse. PLpgSQL cannot to set correct type for record variables and cannot to check a dependent SQLs and expressions. A solution is same like dynamic SQL. Don't use record variable as target when you use refcursor type or disable plpgsql_check for these functions.

``````CREATE OR REPLACE FUNCTION foo(refcur_var refcursor)
RETURNS void AS \$\$
DECLARE
rec_var record;
BEGIN
FETCH refcur_var INTO rec_var; -- this is STOP for plpgsql_check
RAISE NOTICE '%', rec_var;     -- record rec_var is not assigned yet error
``````

In this case a record type should not be used (use known rowtype instead):

``````CREATE OR REPLACE FUNCTION foo(refcur_var refcursor)
RETURNS void AS \$\$
DECLARE
rec_var some_rowtype;
BEGIN
FETCH refcur_var INTO rec_var;
RAISE NOTICE '%', rec_var;
``````

## Temporary tables

plpgsql_check cannot verify queries over temporary tables that are created in plpgsql's function runtime. For this use case it is necessary to create a fake temp table or disable plpgsql_check for this function.

In reality temp tables are stored in own (per user) schema with higher priority than persistent tables. So you can do (with following trick safetly):

``````CREATE OR REPLACE FUNCTION public.disable_dml()
RETURNS trigger
LANGUAGE plpgsql AS \$function\$
BEGIN
RAISE EXCEPTION SQLSTATE '42P01'
USING message = format('this instance of %I table doesn''t allow any DML operation', TG_TABLE_NAME),
hint = format('you should to run "CREATE TEMP TABLE %1\$I(LIKE %1\$I INCLUDING ALL);" statement',
TG_TABLE_NAME);
RETURN NULL;
END;
\$function\$;

CREATE TABLE foo(a int, b int); -- doesn't hold data ever
CREATE TRIGGER foo_disable_dml
BEFORE INSERT OR UPDATE OR DELETE ON foo
EXECUTE PROCEDURE disable_dml();

postgres=# INSERT INTO  foo VALUES(10,20);
ERROR:  this instance of foo table doesn't allow any DML operation
HINT:  you should to run "CREATE TEMP TABLE foo(LIKE foo INCLUDING ALL);" statement
postgres=#

CREATE TABLE
postgres=# INSERT INTO  foo VALUES(10,20);
INSERT 0 1
``````

This trick emulates GLOBAL TEMP tables partially and it allows a statical validation. Other possibility is using a [template foreign data wrapper] (https://github.com/okbob/template_fdw)

You can use pragma `table` and create ephemeral table:

``````BEGIN
CREATE TEMP TABLE xxx(a int);
PERFORM plpgsql_check_pragma('table: xxx(a int)');
INSERT INTO xxx VALUES(10);
``````

Dependency list

A function plpgsql_show_dependency_tb can show all functions, operators and relations used inside processed function:

``````postgres=# select * from plpgsql_show_dependency_tb('testfunc(int,float)');
┌──────────┬───────┬────────┬─────────┬────────────────────────────┐
│   type   │  oid  │ schema │  name   │           params           │
╞══════════╪═══════╪════════╪═════════╪════════════════════════════╡
│ FUNCTION │ 36008 │ public │ myfunc1 │ (integer,double precision) │
│ FUNCTION │ 35999 │ public │ myfunc2 │ (integer,double precision) │
│ OPERATOR │ 36007 │ public │ **      │ (integer,integer)          │
│ RELATION │ 36005 │ public │ myview  │                            │
│ RELATION │ 36002 │ public │ mytable │                            │
└──────────┴───────┴────────┴─────────┴────────────────────────────┘
(4 rows)
``````

Profiler

The plpgsql_check contains simple profiler of plpgsql functions and procedures. It can work with/without a access to shared memory. It depends on `shared_preload_libraries` config. When plpgsql_check was initialized by `shared_preload_libraries`, then it can allocate shared memory, and function's profiles are stored there. When plpgsql_check cannot to allocate shared momory, the profile is stored in session memory.

Due dependencies, `shared_preload_libraries` should to contains `plpgsql` first

``````postgres=# show shared_preload_libraries ;
┌──────────────────────────┐
╞══════════════════════════╡
│ plpgsql,plpgsql_check    │
└──────────────────────────┘
(1 row)
``````

The profiler is active when GUC `plpgsql_check.profiler` is on. The profiler doesn't require shared memory, but if there are not shared memory, then the profile is limmitted just to active session.

When plpgsql_check is initialized by `shared_preload_libraries`, another GUC is available to configure the amount of shared memory used by the profiler: `plpgsql_check.profiler_max_shared_chunks`. This defines the maximum number of statements chunk that can be stored in shared memory. For each plpgsql function (or procedure), the whole content is split into chunks of 30 statements. If needed, multiple chunks can be used to store the whole content of a single function. A single chunk is 1704 bytes. The default value for this GUC is 15000, which should be enough for big projects containing hundred of thousands of statements in plpgsql, and will consume about 24MB of memory. If your project doesn't require that much number of chunks, you can set this parameter to a smaller number in order to decrease the memory usage. The minimum value is 50 (which should consume about 83kB of memory), and the maximum value is 100000 (which should consume about 163MB of memory). Changing this parameter requires a PostgreSQL restart.

The profiler will also retrieve the query identifier for each instruction that contains an expression or optimizable statement. Note that this requires pg_stat_statements, or another similar third-party extension), to be installed. There are some limitations to the query identifier retrieval:

• if a plpgsql expression contains underlying statements, only the top level query identifier will be retrieved
• the profiler doesn't compute query identifier by itself but relies on external extension, such as pg_stat_statements, for that. It means that depending on the external extension behavior, you may not be able to see a query identifier for some statements. That's for instance the case with DDL statements, as pg_stat_statements doesn't expose the query identifier for such queries.
• a query identifier is retrieved only for instructions containing expressions. This means that plpgsql_profiler_function_tb() function can report less query identifier than instructions on a single line.

Attention: A update of shared profiles can decrease performance on servers under higher load.

The profile can be displayed by function `plpgsql_profiler_function_tb`:

``````postgres=# select lineno, avg_time, source from plpgsql_profiler_function_tb('fx(int)');
┌────────┬──────────┬───────────────────────────────────────────────────────────────────┐
│ lineno │ avg_time │                              source                               │
╞════════╪══════════╪═══════════════════════════════════════════════════════════════════╡
│      1 │          │                                                                   │
│      2 │          │ declare result int = 0;                                           │
│      3 │    0.075 │ begin                                                             │
│      4 │    0.202 │   for i in 1..\$1 loop                                             │
│      5 │    0.005 │     select result + i into result; select result + i into result; │
│      6 │          │   end loop;                                                       │
│      7 │        0 │   return result;                                                  │
│      8 │          │ end;                                                              │
└────────┴──────────┴───────────────────────────────────────────────────────────────────┘
(9 rows)
``````

The profile per statements (not per line) can be displayed by function plpgsql_profiler_function_statements_tb:

``````        CREATE OR REPLACE FUNCTION public.fx1(a integer)
RETURNS integer
LANGUAGE plpgsql
1       AS \$function\$
2       begin
3         if a > 10 then
4           raise notice 'ahoj';
5           return -1;
6         else
7           raise notice 'nazdar';
8           return 1;
9         end if;
10      end;
11      \$function\$

postgres=# select stmtid, parent_stmtid, parent_note, lineno, exec_stmts, stmtname
from plpgsql_profiler_function_statements_tb('fx1');
┌────────┬───────────────┬─────────────┬────────┬────────────┬─────────────────┐
│ stmtid │ parent_stmtid │ parent_note │ lineno │ exec_stmts │    stmtname     │
╞════════╪═══════════════╪═════════════╪════════╪════════════╪═════════════════╡
│      0 │             ∅ │ ∅           │      2 │          0 │ statement block │
│      1 │             0 │ body        │      3 │          0 │ IF              │
│      2 │             1 │ then body   │      4 │          0 │ RAISE           │
│      3 │             1 │ then body   │      5 │          0 │ RETURN          │
│      4 │             1 │ else body   │      7 │          0 │ RAISE           │
│      5 │             1 │ else body   │      8 │          0 │ RETURN          │
└────────┴───────────────┴─────────────┴────────┴────────────┴─────────────────┘
(6 rows)
``````

All stored profiles can be displayed by calling function `plpgsql_profiler_functions_all`:

``````postgres=# select * from plpgsql_profiler_functions_all();
┌───────────────────────┬────────────┬────────────┬──────────┬─────────────┬──────────┬──────────┐
│        funcoid        │ exec_count │ total_time │ avg_time │ stddev_time │ min_time │ max_time │
╞═══════════════════════╪════════════╪════════════╪══════════╪═════════════╪══════════╪══════════╡
│ fxx(double precision) │          1 │       0.01 │     0.01 │        0.00 │     0.01 │     0.01 │
└───────────────────────┴────────────┴────────────┴──────────┴─────────────┴──────────┴──────────┘
(1 row)
``````

There are two functions for cleaning stored profiles: `plpgsql_profiler_reset_all()` and `plpgsql_profiler_reset(regprocedure)`.

## Coverage metrics

plpgsql_check provides two functions:

• `plpgsql_coverage_statements(name)`
• `plpgsql_coverage_branches(name)`

## Note

There is another very good PLpgSQL profiler - https://bitbucket.org/openscg/plprofiler

My extension is designed to be simple for use and practical. Nothing more or less.

plprofiler is more complex. It build call graphs and from this graph it can creates flame graph of execution times.

Both extensions can be used together with buildin PostgreSQL's feature - tracking functions.

``````set track_functions to 'pl';
...
select * from pg_stat_user_functions;
``````

Tracer

plpgsql_check provides a tracing possibility - in this mode you can see notices on start or end functions (terse and default verbosity) and start or end statements (verbose verbosity). For default and verbose verbosity the content of function arguments is displayed. The content of related variables are displayed when verbosity is verbose.

``````postgres=# do \$\$ begin perform fx(10,null, 'now', e'stěhule'); end; \$\$;
NOTICE:  #0 ->> start of inline_code_block (Oid=0)
NOTICE:  #2   ->> start of function fx(integer,integer,date,text) (Oid=16405)
NOTICE:  #2        call by inline_code_block line 1 at PERFORM
NOTICE:  #2       "a" => '10', "b" => null, "c" => '2020-08-03', "d" => 'stěhule'
NOTICE:  #4     ->> start of function fx(integer) (Oid=16404)
NOTICE:  #4          call by fx(integer,integer,date,text) line 1 at PERFORM
NOTICE:  #4         "a" => '10'
NOTICE:  #4     <<- end of function fx (elapsed time=0.098 ms)
NOTICE:  #2   <<- end of function fx (elapsed time=0.399 ms)
NOTICE:  #0 <<- end of block (elapsed time=0.754 ms)
``````

The number after `#` is a execution frame counter (this number is related to deep of error context stack). It allows to pair start end and of function.

Tracing is enabled by setting `plpgsql_check.tracer` to `on`. Attention - enabling this behaviour has significant negative impact on performance (unlike the profiler). You can set a level for output used by tracer `plpgsql_check.tracer_errlevel` (default is `notice`). The output content is limited by length specified by `plpgsql_check.tracer_variable_max_length` configuration variable.

In terse verbose mode the output is reduced:

``````postgres=# set plpgsql_check.tracer_verbosity TO terse;
SET
postgres=# do \$\$ begin perform fx(10,null, 'now', e'stěhule'); end; \$\$;
NOTICE:  #0 start of inline code block (oid=0)
NOTICE:  #2 start of fx (oid=16405)
NOTICE:  #4 start of fx (oid=16404)
NOTICE:  #4 end of fx
NOTICE:  #2 end of fx
NOTICE:  #0 end of inline code block
``````

In verbose mode the output is extended about statement details:

``````postgres=# do \$\$ begin perform fx(10,null, 'now', e'stěhule'); end; \$\$;
NOTICE:  #0            ->> start of block inline_code_block (oid=0)
NOTICE:  #0.1       1  --> start of PERFORM
NOTICE:  #2              ->> start of function fx(integer,integer,date,text) (oid=16405)
NOTICE:  #2                   call by inline_code_block line 1 at PERFORM
NOTICE:  #2                  "a" => '10', "b" => null, "c" => '2020-08-04', "d" => 'stěhule'
NOTICE:  #2.1       1    --> start of PERFORM
NOTICE:  #2.1                "a" => '10'
NOTICE:  #4                ->> start of function fx(integer) (oid=16404)
NOTICE:  #4                     call by fx(integer,integer,date,text) line 1 at PERFORM
NOTICE:  #4                    "a" => '10'
NOTICE:  #4.1       6      --> start of assignment
NOTICE:  #4.1                  "a" => '10', "b" => '20'
NOTICE:  #4.1              <-- end of assignment (elapsed time=0.076 ms)
NOTICE:  #4.1                  "res" => '130'
NOTICE:  #4.2       7      --> start of RETURN
NOTICE:  #4.2                  "res" => '130'
NOTICE:  #4.2              <-- end of RETURN (elapsed time=0.054 ms)
NOTICE:  #4                <<- end of function fx (elapsed time=0.373 ms)
NOTICE:  #2.1            <-- end of PERFORM (elapsed time=0.589 ms)
NOTICE:  #2              <<- end of function fx (elapsed time=0.727 ms)
NOTICE:  #0.1          <-- end of PERFORM (elapsed time=1.147 ms)
NOTICE:  #0            <<- end of block (elapsed time=1.286 ms)
``````

Special feature of tracer is tracing of `ASSERT` statement when `plpgsql_check.trace_assert` is `on`. When `plpgsql_check.trace_assert_verbosity` is `DEFAULT`, then all function's or procedure's variables are displayed when assert expression is false. When this configuration is `VERBOSE` then all variables from all plpgsql frames are displayed. This behaviour is independent on `plpgsql.check_asserts` value. It can be used, although the assertions are disabled in plpgsql runtime.

``````postgres=# set plpgsql_check.tracer to off;
postgres=# set plpgsql_check.trace_assert_verbosity TO verbose;

postgres=# do \$\$ begin perform fx(10,null, 'now', e'stěhule'); end; \$\$;
NOTICE:  #4 PLpgSQL assert expression (false) on line 12 of fx(integer) is false
NOTICE:   "a" => '10', "res" => null, "b" => '20'
NOTICE:  #2 PL/pgSQL function fx(integer,integer,date,text) line 1 at PERFORM
NOTICE:   "a" => '10', "b" => null, "c" => '2020-08-05', "d" => 'stěhule'
NOTICE:  #0 PL/pgSQL function inline_code_block line 1 at PERFORM
ERROR:  assertion failed
CONTEXT:  PL/pgSQL function fx(integer) line 12 at ASSERT
SQL statement "SELECT fx(a)"
PL/pgSQL function fx(integer,integer,date,text) line 1 at PERFORM
SQL statement "SELECT fx(10,null, 'now', e'stěhule')"
PL/pgSQL function inline_code_block line 1 at PERFORM

postgres=# set plpgsql.check_asserts to off;
SET
postgres=# do \$\$ begin perform fx(10,null, 'now', e'stěhule'); end; \$\$;
NOTICE:  #4 PLpgSQL assert expression (false) on line 12 of fx(integer) is false
NOTICE:   "a" => '10', "res" => null, "b" => '20'
NOTICE:  #2 PL/pgSQL function fx(integer,integer,date,text) line 1 at PERFORM
NOTICE:   "a" => '10', "b" => null, "c" => '2020-08-05', "d" => 'stěhule'
NOTICE:  #0 PL/pgSQL function inline_code_block line 1 at PERFORM
DO
``````

## Attention - SECURITY

Tracer prints content of variables or function arguments. For security definer function, this content can hold security sensitive data. This is reason why tracer is disabled by default and should be enabled only with super user rights `plpgsql_check.enable_tracer`.

Pragma

You can configure plpgsql_check behave inside checked function with "pragma" function. This is a analogy of PL/SQL or ADA language of PRAGMA feature. PLpgSQL doesn't support PRAGMA, but plpgsql_check detects function named `plpgsql_check_pragma` and get options from parameters of this function. These plpgsql_check options are valid to end of group of statements.

``````CREATE OR REPLACE FUNCTION test()
RETURNS void AS \$\$
BEGIN
...
-- for following statements disable check
PERFORM plpgsql_check_pragma('disable:check');
...
-- enable check again
PERFORM plpgsql_check_pragma('enable:check');
...
END;
\$\$ LANGUAGE plpgsql;
``````

The function `plpgsql_check_pragma` is immutable function that returns one. It is defined by `plpgsql_check` extension. You can declare alternative `plpgsql_check_pragma` function like:

``````CREATE OR REPLACE FUNCTION plpgsql_check_pragma(VARIADIC args[])
RETURNS int AS \$\$
SELECT 1
\$\$ LANGUAGE sql IMMUTABLE;
``````

Using pragma function in declaration part of top block sets options on function level too.

``````CREATE OR REPLACE FUNCTION test()
RETURNS void AS \$\$
DECLARE
aux int := plpgsql_check_pragma('disable:extra_warnings');
...
``````

Shorter syntax for pragma is supported too:

``````CREATE OR REPLACE FUNCTION test()
RETURNS void AS \$\$
DECLARE r record;
BEGIN
PERFORM 'PRAGMA:TYPE:r (a int, b int)';
PERFORM 'PRAGMA:TABLE: x (like pg_class)';
...
``````

## Supported pragmas

`echo:str` - print string (for testing)

`status:check`,`status:tracer`, `status:other_warnings`, `status:performance_warnings`, `status:extra_warnings`,`status:security_warnings`

`enable:check`,`enable:tracer`, `enable:other_warnings`, `enable:performance_warnings`, `enable:extra_warnings`,`enable:security_warnings`

`disable:check`,`disable:tracer`, `disable:other_warnings`, `disable:performance_warnings`, `disable:extra_warnings`,`disable:security_warnings`

`type:varname typename` or `type:varname (fieldname type, ...)` - set type to variable of record type

`table: name (column_name type, ...)` or `table: name (like tablename)` - create ephereal table

Pragmas `enable:tracer` and `disable:tracer`are active for Postgres 12 and higher

Compilation

You need a development environment for PostgreSQL extensions:

``````make clean
make install
``````

result:

``````[pavel@localhost plpgsql_check]\$ make USE_PGXS=1 clean
rm -f plpgsql_check.so   libplpgsql_check.a  libplpgsql_check.pc
rm -f plpgsql_check.o
rm -rf results/ regression.diffs regression.out tmp_check/ log/
[pavel@localhost plpgsql_check]\$ make USE_PGXS=1 all
clang -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fpic -I/usr/local/pgsql/lib/pgxs/src/makefiles/../../src/pl/plpgsql/src -I. -I./ -I/usr/local/pgsql/include/server -I/usr/local/pgsql/include/internal -D_GNU_SOURCE   -c -o plpgsql_check.o plpgsql_check.c
clang -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fpic -I/usr/local/pgsql/lib/pgxs/src/makefiles/../../src/pl/plpgsql/src -shared -o plpgsql_check.so plpgsql_check.o -L/usr/local/pgsql/lib -Wl,--as-needed -Wl,-rpath,'/usr/local/pgsql/lib',--enable-new-dtags
[pavel@localhost plpgsql_check]\$ su root
[root@localhost plpgsql_check]# make USE_PGXS=1 install
/usr/bin/mkdir -p '/usr/local/pgsql/lib'
/usr/bin/mkdir -p '/usr/local/pgsql/share/extension'
/usr/bin/mkdir -p '/usr/local/pgsql/share/extension'
/usr/bin/install -c -m 755  plpgsql_check.so '/usr/local/pgsql/lib/plpgsql_check.so'
/usr/bin/install -c -m 644 plpgsql_check.control '/usr/local/pgsql/share/extension/'
/usr/bin/install -c -m 644 plpgsql_check--0.9.sql '/usr/local/pgsql/share/extension/'
[root@localhost plpgsql_check]# exit
[pavel@localhost plpgsql_check]\$ make USE_PGXS=1 installcheck
/usr/local/pgsql/lib/pgxs/src/makefiles/../../src/test/regress/pg_regress --inputdir=./ --psqldir='/usr/local/pgsql/bin'    --dbname=pl_regression --load-language=plpgsql --dbname=contrib_regression plpgsql_check_passive plpgsql_check_active plpgsql_check_active-9.5
(using postmaster on Unix socket, default port)
============== dropping database "contrib_regression" ==============
DROP DATABASE
============== creating database "contrib_regression" ==============
CREATE DATABASE
ALTER DATABASE
============== installing plpgsql                     ==============
CREATE LANGUAGE
============== running regression test queries        ==============
test plpgsql_check_passive    ... ok
test plpgsql_check_active     ... ok
test plpgsql_check_active-9.5 ... ok

=====================
All 3 tests passed.
=====================
``````

## Compilation on Ubuntu

Sometimes successful compilation can require libicu-dev package (PostgreSQL 10 and higher - when pg was compiled with ICU support)

``````sudo apt install libicu-dev
``````

## Compilation plpgsql_check on Windows

You can check precompiled dll libraries http://okbob.blogspot.cz/2015/02/plpgsqlcheck-is-available-for-microsoft.html

or compile by self:

4. Build plpgsql_check.dll
5. Install plugin
6. copy `plpgsql_check.dll` to `PostgreSQL\14\lib`
7. copy `plpgsql_check.control` and `plpgsql_check--2.1.sql` to `PostgreSQL\14\share\extension`

## Checked on

• gcc on Linux (against all supported PostgreSQL)
• clang 3.4 on Linux (against PostgreSQL 10)
• for success regress tests the PostgreSQL 10 or higher is required

Compilation against PostgreSQL 10 requires libICU!

Licence

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Note

If you like it, send a postcard to address

``````Pavel Stehule
Skalice 12
256 01 Benesov u Prahy
Czech Republic
``````

I invite any questions, comments, bug reports, patches on mail address pavel.stehule@gmail.com

Author: okbob
Source Code: https://github.com/okbob/plpgsql_check

1648803600

## plpgsql_check

I founded this project, because I wanted to publish the code I wrote in the last two years, when I tried to write enhanced checking for PostgreSQL upstream. It was not fully successful - integration into upstream requires some larger plpgsql refactoring - probably it will not be done in next years (now is Dec 2013). But written code is fully functional and can be used in production (and it is used in production). So, I created this extension to be available for all plpgsql developers.

If you like it and if you would to join to development of this extension, register yourself to postgresql extension hacking google group.

Features

• check fields of referenced database objects and types inside embedded SQL
• using correct types of function parameters
• unused variables and function argumens, unmodified OUT argumens
• partially detection of dead code (due RETURN command)
• detection of missing RETURN command in function
• try to identify unwanted hidden casts, that can be performance issue like unused indexes
• possibility to collect relations and functions used by function
• possibility to check EXECUTE stmt agaist SQL injection vulnerability

I invite any ideas, patches, bugreports.

plpgsql_check is next generation of plpgsql_lint. It allows to check source code by explicit call plpgsql_check_function.

PostgreSQL PostgreSQL 10, 11, 12, 13 and 14 are supported.

The SQL statements inside PL/pgSQL functions are checked by validator for semantic errors. These errors can be found by plpgsql_check_function:

Active mode

``````postgres=# CREATE EXTENSION plpgsql_check;
postgres=# CREATE TABLE t1(a int, b int);
CREATE TABLE

postgres=#
CREATE OR REPLACE FUNCTION public.f1()
RETURNS void
LANGUAGE plpgsql
AS \$function\$
DECLARE r record;
BEGIN
FOR r IN SELECT * FROM t1
LOOP
RAISE NOTICE '%', r.c; -- there is bug - table t1 missing "c" column
END LOOP;
END;
\$function\$;

CREATE FUNCTION

postgres=# select f1(); -- execution doesn't find a bug due to empty table t1
f1
────

(1 row)

postgres=# \x
Expanded display is on.
postgres=# select * from plpgsql_check_function_tb('f1()');
─[ RECORD 1 ]───────────────────────────
functionid │ f1
lineno     │ 6
statement  │ RAISE
sqlstate   │ 42703
message    │ record "r" has no field "c"
detail     │ [null]
hint       │ [null]
level      │ error
position   │ 0
query      │ [null]

postgres=# \sf+ f1
CREATE OR REPLACE FUNCTION public.f1()
RETURNS void
LANGUAGE plpgsql
1       AS \$function\$
2       DECLARE r record;
3       BEGIN
4         FOR r IN SELECT * FROM t1
5         LOOP
6           RAISE NOTICE '%', r.c; -- there is bug - table t1 missing "c" column
7         END LOOP;
8       END;
9       \$function\$
``````

Function plpgsql_check_function() has three possible formats: text, json or xml

``````select * from plpgsql_check_function('f1()', fatal_errors := false);
plpgsql_check_function
------------------------------------------------------------------------
error:42703:4:SQL statement:column "c" of relation "t1" does not exist
Query: update t1 set c = 30
--                   ^
error:42P01:7:RAISE:missing FROM-clause entry for table "r"
Query: SELECT r.c
--            ^
error:42601:7:RAISE:too few parameters specified for RAISE
(7 rows)

postgres=# select * from plpgsql_check_function('fx()', format:='xml');
plpgsql_check_function
────────────────────────────────────────────────────────────────
<Function oid="16400">                                        ↵
<Issue>                                                     ↵
<Level>error</level>                                      ↵
<Sqlstate>42P01</Sqlstate>                                ↵
<Message>relation "foo111" does not exist</Message>       ↵
<Stmt lineno="3">RETURN</Stmt>                            ↵
<Query position="23">SELECT (select a from foo111)</Query>↵
</Issue>                                                    ↵
</Function>
(1 row)
``````

## Arguments

You can set level of warnings via function's parameters:

### Mandatory arguments

• function name or function signature - these functions requires function specification. Any function in PostgreSQL can be specified by Oid or by name or by signature. When you know oid or complete function's signature, you can use a regprocedure type parameter like `'fx()'::regprocedure` or `16799::regprocedure`. Possible alternative is using a name only, when function's name is unique - like `'fx'`. When the name is not unique or the function doesn't exists it raises a error.

### Optional arguments

`relid DEFAULT 0` - oid of relation assigned with trigger function. It is necessary for check of any trigger function.

`fatal_errors boolean DEFAULT true` - stop on first error

`other_warnings boolean DEFAULT true` - show warnings like different attributes number in assignmenet on left and right side, variable overlaps function's parameter, unused variables, unwanted casting, ..

`extra_warnings boolean DEFAULT true` - show warnings like missing `RETURN`, shadowed variables, dead code, never read (unused) function's parameter, unmodified variables, modified auto variables, ..

`performance_warnings boolean DEFAULT false` - performance related warnings like declared type with type modificator, casting, implicit casts in where clause (can be reason why index is not used), ..

`security_warnings boolean DEFAULT false` - security related checks like SQL injection vulnerability detection

`anyelementtype regtype DEFAULT 'int'` - a real type used instead anyelement type

`anyenumtype regtype DEFAULT '-'` - a real type used instead anyenum type

`anyrangetype regtype DEFAULT 'int4range'` - a real type used instead anyrange type

`anycompatibletype DEFAULT 'int'` - a real type used instead anycompatible type

`anycompatiblerangetype DEFAULT 'int4range'` - a real type used instead anycompatible range type

`without_warnings DEFAULT false` - disable all warnings

`all_warnings DEFAULT false` - enable all warnings

`newtable DEFAULT NULL`, `oldtable DEFAULT NULL` - the names of NEW or OLD transitive tables. These parameters are required when transitive tables are used.

## Triggers

When you want to check any trigger, you have to enter a relation that will be used together with trigger function

``````CREATE TABLE bar(a int, b int);

postgres=# \sf+ foo_trg
CREATE OR REPLACE FUNCTION public.foo_trg()
RETURNS trigger
LANGUAGE plpgsql
1       AS \$function\$
2       BEGIN
3         NEW.c := NEW.a + NEW.b;
4         RETURN NEW;
5       END;
6       \$function\$
``````

Missing relation specification

``````postgres=# select * from plpgsql_check_function('foo_trg()');
ERROR:  missing trigger relation
HINT:  Trigger relation oid must be valid
``````

Correct trigger checking (with specified relation)

``````postgres=# select * from plpgsql_check_function('foo_trg()', 'bar');
plpgsql_check_function
--------------------------------------------------------
error:42703:3:assignment:record "new" has no field "c"
(1 row)
``````

For triggers with transitive tables you can set a `oldtable` or `newtable` parameters:

``````create or replace function footab_trig_func()
returns trigger as \$\$
declare x int;
begin
if false then
-- should be ok;
select count(*) from newtab into x;

-- should fail;
select count(*) from newtab where d = 10 into x;
end if;
return null;
end;
\$\$ language plpgsql;

select * from plpgsql_check_function('footab_trig_func','footab', newtable := 'newtab');
``````

## Mass check

You can use the plpgsql_check_function for mass check functions and mass check triggers. Please, test following queries:

``````-- check all nontrigger plpgsql functions
SELECT p.oid, p.proname, plpgsql_check_function(p.oid)
FROM pg_catalog.pg_namespace n
JOIN pg_catalog.pg_proc p ON pronamespace = n.oid
JOIN pg_catalog.pg_language l ON p.prolang = l.oid
WHERE l.lanname = 'plpgsql' AND p.prorettype <> 2279;
``````

or

``````SELECT p.proname, tgrelid::regclass, cf.*
FROM pg_proc p
JOIN pg_trigger t ON t.tgfoid = p.oid
JOIN pg_language l ON p.prolang = l.oid
JOIN pg_namespace n ON p.pronamespace = n.oid,
LATERAL plpgsql_check_function(p.oid, t.tgrelid) cf
WHERE n.nspname = 'public' and l.lanname = 'plpgsql'
``````

or

``````-- check all plpgsql functions (functions or trigger functions with defined triggers)
SELECT
(pcf).functionid::regprocedure, (pcf).lineno, (pcf).statement,
(pcf).sqlstate, (pcf).message, (pcf).detail, (pcf).hint, (pcf).level,
(pcf)."position", (pcf).query, (pcf).context
FROM
(
SELECT
plpgsql_check_function_tb(pg_proc.oid, COALESCE(pg_trigger.tgrelid, 0)) AS pcf
FROM pg_proc
LEFT JOIN pg_trigger
ON (pg_trigger.tgfoid = pg_proc.oid)
WHERE
prolang = (SELECT lang.oid FROM pg_language lang WHERE lang.lanname = 'plpgsql') AND
pronamespace <> (SELECT nsp.oid FROM pg_namespace nsp WHERE nsp.nspname = 'pg_catalog') AND
-- ignore unused triggers
(pg_proc.prorettype <> (SELECT typ.oid FROM pg_type typ WHERE typ.typname = 'trigger') OR
pg_trigger.tgfoid IS NOT NULL)
OFFSET 0
) ss
ORDER BY (pcf).functionid::regprocedure::text, (pcf).lineno
``````

Passive mode

Functions should be checked on start - plpgsql_check module must be loaded.

## Configuration

``````plpgsql_check.mode = [ disabled | by_function | fresh_start | every_start ]
plpgsql_check.fatal_errors = [ yes | no ]

plpgsql_check.show_nonperformance_warnings = false
plpgsql_check.show_performance_warnings = false
``````

Default mode is by_function, that means that the enhanced check is done only in active mode - by plpgsql_check_function. `fresh_start` means cold start.

You can enable passive mode by

``````load 'plpgsql'; -- 1.1 and higher doesn't need it
set plpgsql_check.mode = 'every_start';

SELECT fx(10); -- run functions - function is checked before runtime starts it
``````

Limits

plpgsql_check should find almost all errors on really static code. When developer use some PLpgSQL's dynamic features like dynamic SQL or record data type, then false positives are possible. These should be rare - in well written code - and then the affected function should be redesigned or plpgsql_check should be disabled for this function.

``````CREATE OR REPLACE FUNCTION f1()
RETURNS void AS \$\$
DECLARE r record;
BEGIN
FOR r IN EXECUTE 'SELECT * FROM t1'
LOOP
RAISE NOTICE '%', r.c;
END LOOP;
END;
\$\$ LANGUAGE plpgsql SET plpgsql.enable_check TO false;
``````

A usage of plpgsql_check adds a small overhead (in enabled passive mode) and you should use it only in develop or preprod environments.

## Dynamic SQL

This module doesn't check queries that are assembled in runtime. It is not possible to identify results of dynamic queries - so plpgsql_check cannot to set correct type to record variables and cannot to check a dependent SQLs and expressions.

When type of record's variable is not know, you can assign it explicitly with pragma `type`:

``````DECLARE r record;
BEGIN
EXECUTE format('SELECT * FROM %I', _tablename) INTO r;
PERFORM plpgsql_check_pragma('type: r (id int, processed bool)');
IF NOT r.processed THEN
...
``````

Attention: The SQL injection check can detect only some SQL injection vulnerabilities. This tool cannot be used for security audit! Some issues should not be detected. This check can raise false alarms too - probably when variable is sanitized by other command or when value is of some compose type.

## Refcursors

plpgsql_check should not to detect structure of referenced cursors. A reference on cursor in PLpgSQL is implemented as name of global cursor. In check time, the name is not known (not in all possibilities), and global cursor doesn't exist. It is significant break for any static analyse. PLpgSQL cannot to set correct type for record variables and cannot to check a dependent SQLs and expressions. A solution is same like dynamic SQL. Don't use record variable as target when you use refcursor type or disable plpgsql_check for these functions.

``````CREATE OR REPLACE FUNCTION foo(refcur_var refcursor)
RETURNS void AS \$\$
DECLARE
rec_var record;
BEGIN
FETCH refcur_var INTO rec_var; -- this is STOP for plpgsql_check
RAISE NOTICE '%', rec_var;     -- record rec_var is not assigned yet error
``````

In this case a record type should not be used (use known rowtype instead):

``````CREATE OR REPLACE FUNCTION foo(refcur_var refcursor)
RETURNS void AS \$\$
DECLARE
rec_var some_rowtype;
BEGIN
FETCH refcur_var INTO rec_var;
RAISE NOTICE '%', rec_var;
``````

## Temporary tables

plpgsql_check cannot verify queries over temporary tables that are created in plpgsql's function runtime. For this use case it is necessary to create a fake temp table or disable plpgsql_check for this function.

In reality temp tables are stored in own (per user) schema with higher priority than persistent tables. So you can do (with following trick safetly):

``````CREATE OR REPLACE FUNCTION public.disable_dml()
RETURNS trigger
LANGUAGE plpgsql AS \$function\$
BEGIN
RAISE EXCEPTION SQLSTATE '42P01'
USING message = format('this instance of %I table doesn''t allow any DML operation', TG_TABLE_NAME),
hint = format('you should to run "CREATE TEMP TABLE %1\$I(LIKE %1\$I INCLUDING ALL);" statement',
TG_TABLE_NAME);
RETURN NULL;
END;
\$function\$;

CREATE TABLE foo(a int, b int); -- doesn't hold data ever
CREATE TRIGGER foo_disable_dml
BEFORE INSERT OR UPDATE OR DELETE ON foo
EXECUTE PROCEDURE disable_dml();

postgres=# INSERT INTO  foo VALUES(10,20);
ERROR:  this instance of foo table doesn't allow any DML operation
HINT:  you should to run "CREATE TEMP TABLE foo(LIKE foo INCLUDING ALL);" statement
postgres=#

CREATE TABLE
postgres=# INSERT INTO  foo VALUES(10,20);
INSERT 0 1
``````

This trick emulates GLOBAL TEMP tables partially and it allows a statical validation. Other possibility is using a [template foreign data wrapper] (https://github.com/okbob/template_fdw)

You can use pragma `table` and create ephemeral table:

``````BEGIN
CREATE TEMP TABLE xxx(a int);
PERFORM plpgsql_check_pragma('table: xxx(a int)');
INSERT INTO xxx VALUES(10);
``````

Dependency list

A function plpgsql_show_dependency_tb can show all functions, operators and relations used inside processed function:

``````postgres=# select * from plpgsql_show_dependency_tb('testfunc(int,float)');
┌──────────┬───────┬────────┬─────────┬────────────────────────────┐
│   type   │  oid  │ schema │  name   │           params           │
╞══════════╪═══════╪════════╪═════════╪════════════════════════════╡
│ FUNCTION │ 36008 │ public │ myfunc1 │ (integer,double precision) │
│ FUNCTION │ 35999 │ public │ myfunc2 │ (integer,double precision) │
│ OPERATOR │ 36007 │ public │ **      │ (integer,integer)          │
│ RELATION │ 36005 │ public │ myview  │                            │
│ RELATION │ 36002 │ public │ mytable │                            │
└──────────┴───────┴────────┴─────────┴────────────────────────────┘
(4 rows)
``````

Profiler

The plpgsql_check contains simple profiler of plpgsql functions and procedures. It can work with/without a access to shared memory. It depends on `shared_preload_libraries` config. When plpgsql_check was initialized by `shared_preload_libraries`, then it can allocate shared memory, and function's profiles are stored there. When plpgsql_check cannot to allocate shared momory, the profile is stored in session memory.

Due dependencies, `shared_preload_libraries` should to contains `plpgsql` first

``````postgres=# show shared_preload_libraries ;
┌──────────────────────────┐
╞══════════════════════════╡
│ plpgsql,plpgsql_check    │
└──────────────────────────┘
(1 row)
``````

The profiler is active when GUC `plpgsql_check.profiler` is on. The profiler doesn't require shared memory, but if there are not shared memory, then the profile is limmitted just to active session.

When plpgsql_check is initialized by `shared_preload_libraries`, another GUC is available to configure the amount of shared memory used by the profiler: `plpgsql_check.profiler_max_shared_chunks`. This defines the maximum number of statements chunk that can be stored in shared memory. For each plpgsql function (or procedure), the whole content is split into chunks of 30 statements. If needed, multiple chunks can be used to store the whole content of a single function. A single chunk is 1704 bytes. The default value for this GUC is 15000, which should be enough for big projects containing hundred of thousands of statements in plpgsql, and will consume about 24MB of memory. If your project doesn't require that much number of chunks, you can set this parameter to a smaller number in order to decrease the memory usage. The minimum value is 50 (which should consume about 83kB of memory), and the maximum value is 100000 (which should consume about 163MB of memory). Changing this parameter requires a PostgreSQL restart.

The profiler will also retrieve the query identifier for each instruction that contains an expression or optimizable statement. Note that this requires pg_stat_statements, or another similar third-party extension), to be installed. There are some limitations to the query identifier retrieval:

• if a plpgsql expression contains underlying statements, only the top level query identifier will be retrieved
• the profiler doesn't compute query identifier by itself but relies on external extension, such as pg_stat_statements, for that. It means that depending on the external extension behavior, you may not be able to see a query identifier for some statements. That's for instance the case with DDL statements, as pg_stat_statements doesn't expose the query identifier for such queries.
• a query identifier is retrieved only for instructions containing expressions. This means that plpgsql_profiler_function_tb() function can report less query identifier than instructions on a single line.

Attention: A update of shared profiles can decrease performance on servers under higher load.

The profile can be displayed by function `plpgsql_profiler_function_tb`:

``````postgres=# select lineno, avg_time, source from plpgsql_profiler_function_tb('fx(int)');
┌────────┬──────────┬───────────────────────────────────────────────────────────────────┐
│ lineno │ avg_time │                              source                               │
╞════════╪══════════╪═══════════════════════════════════════════════════════════════════╡
│      1 │          │                                                                   │
│      2 │          │ declare result int = 0;                                           │
│      3 │    0.075 │ begin                                                             │
│      4 │    0.202 │   for i in 1..\$1 loop                                             │
│      5 │    0.005 │     select result + i into result; select result + i into result; │
│      6 │          │   end loop;                                                       │
│      7 │        0 │   return result;                                                  │
│      8 │          │ end;                                                              │
└────────┴──────────┴───────────────────────────────────────────────────────────────────┘
(9 rows)
``````

The profile per statements (not per line) can be displayed by function plpgsql_profiler_function_statements_tb:

``````        CREATE OR REPLACE FUNCTION public.fx1(a integer)
RETURNS integer
LANGUAGE plpgsql
1       AS \$function\$
2       begin
3         if a > 10 then
4           raise notice 'ahoj';
5           return -1;
6         else
7           raise notice 'nazdar';
8           return 1;
9         end if;
10      end;
11      \$function\$

postgres=# select stmtid, parent_stmtid, parent_note, lineno, exec_stmts, stmtname
from plpgsql_profiler_function_statements_tb('fx1');
┌────────┬───────────────┬─────────────┬────────┬────────────┬─────────────────┐
│ stmtid │ parent_stmtid │ parent_note │ lineno │ exec_stmts │    stmtname     │
╞════════╪═══════════════╪═════════════╪════════╪════════════╪═════════════════╡
│      0 │             ∅ │ ∅           │      2 │          0 │ statement block │
│      1 │             0 │ body        │      3 │          0 │ IF              │
│      2 │             1 │ then body   │      4 │          0 │ RAISE           │
│      3 │             1 │ then body   │      5 │          0 │ RETURN          │
│      4 │             1 │ else body   │      7 │          0 │ RAISE           │
│      5 │             1 │ else body   │      8 │          0 │ RETURN          │
└────────┴───────────────┴─────────────┴────────┴────────────┴─────────────────┘
(6 rows)
``````

All stored profiles can be displayed by calling function `plpgsql_profiler_functions_all`:

``````postgres=# select * from plpgsql_profiler_functions_all();
┌───────────────────────┬────────────┬────────────┬──────────┬─────────────┬──────────┬──────────┐
│        funcoid        │ exec_count │ total_time │ avg_time │ stddev_time │ min_time │ max_time │
╞═══════════════════════╪════════════╪════════════╪══════════╪═════════════╪══════════╪══════════╡
│ fxx(double precision) │          1 │       0.01 │     0.01 │        0.00 │     0.01 │     0.01 │
└───────────────────────┴────────────┴────────────┴──────────┴─────────────┴──────────┴──────────┘
(1 row)
``````

There are two functions for cleaning stored profiles: `plpgsql_profiler_reset_all()` and `plpgsql_profiler_reset(regprocedure)`.

## Coverage metrics

plpgsql_check provides two functions:

• `plpgsql_coverage_statements(name)`
• `plpgsql_coverage_branches(name)`

## Note

There is another very good PLpgSQL profiler - https://bitbucket.org/openscg/plprofiler

My extension is designed to be simple for use and practical. Nothing more or less.

plprofiler is more complex. It build call graphs and from this graph it can creates flame graph of execution times.

Both extensions can be used together with buildin PostgreSQL's feature - tracking functions.

``````set track_functions to 'pl';
...
select * from pg_stat_user_functions;
``````

Tracer

plpgsql_check provides a tracing possibility - in this mode you can see notices on start or end functions (terse and default verbosity) and start or end statements (verbose verbosity). For default and verbose verbosity the content of function arguments is displayed. The content of related variables are displayed when verbosity is verbose.

``````postgres=# do \$\$ begin perform fx(10,null, 'now', e'stěhule'); end; \$\$;
NOTICE:  #0 ->> start of inline_code_block (Oid=0)
NOTICE:  #2   ->> start of function fx(integer,integer,date,text) (Oid=16405)
NOTICE:  #2        call by inline_code_block line 1 at PERFORM
NOTICE:  #2       "a" => '10', "b" => null, "c" => '2020-08-03', "d" => 'stěhule'
NOTICE:  #4     ->> start of function fx(integer) (Oid=16404)
NOTICE:  #4          call by fx(integer,integer,date,text) line 1 at PERFORM
NOTICE:  #4         "a" => '10'
NOTICE:  #4     <<- end of function fx (elapsed time=0.098 ms)
NOTICE:  #2   <<- end of function fx (elapsed time=0.399 ms)
NOTICE:  #0 <<- end of block (elapsed time=0.754 ms)
``````

The number after `#` is a execution frame counter (this number is related to deep of error context stack). It allows to pair start end and of function.

Tracing is enabled by setting `plpgsql_check.tracer` to `on`. Attention - enabling this behaviour has significant negative impact on performance (unlike the profiler). You can set a level for output used by tracer `plpgsql_check.tracer_errlevel` (default is `notice`). The output content is limited by length specified by `plpgsql_check.tracer_variable_max_length` configuration variable.

In terse verbose mode the output is reduced:

``````postgres=# set plpgsql_check.tracer_verbosity TO terse;
SET
postgres=# do \$\$ begin perform fx(10,null, 'now', e'stěhule'); end; \$\$;
NOTICE:  #0 start of inline code block (oid=0)
NOTICE:  #2 start of fx (oid=16405)
NOTICE:  #4 start of fx (oid=16404)
NOTICE:  #4 end of fx
NOTICE:  #2 end of fx
NOTICE:  #0 end of inline code block
``````

In verbose mode the output is extended about statement details:

``````postgres=# do \$\$ begin perform fx(10,null, 'now', e'stěhule'); end; \$\$;
NOTICE:  #0            ->> start of block inline_code_block (oid=0)
NOTICE:  #0.1       1  --> start of PERFORM
NOTICE:  #2              ->> start of function fx(integer,integer,date,text) (oid=16405)
NOTICE:  #2                   call by inline_code_block line 1 at PERFORM
NOTICE:  #2                  "a" => '10', "b" => null, "c" => '2020-08-04', "d" => 'stěhule'
NOTICE:  #2.1       1    --> start of PERFORM
NOTICE:  #2.1                "a" => '10'
NOTICE:  #4                ->> start of function fx(integer) (oid=16404)
NOTICE:  #4                     call by fx(integer,integer,date,text) line 1 at PERFORM
NOTICE:  #4                    "a" => '10'
NOTICE:  #4.1       6      --> start of assignment
NOTICE:  #4.1                  "a" => '10', "b" => '20'
NOTICE:  #4.1              <-- end of assignment (elapsed time=0.076 ms)
NOTICE:  #4.1                  "res" => '130'
NOTICE:  #4.2       7      --> start of RETURN
NOTICE:  #4.2                  "res" => '130'
NOTICE:  #4.2              <-- end of RETURN (elapsed time=0.054 ms)
NOTICE:  #4                <<- end of function fx (elapsed time=0.373 ms)
NOTICE:  #2.1            <-- end of PERFORM (elapsed time=0.589 ms)
NOTICE:  #2              <<- end of function fx (elapsed time=0.727 ms)
NOTICE:  #0.1          <-- end of PERFORM (elapsed time=1.147 ms)
NOTICE:  #0            <<- end of block (elapsed time=1.286 ms)
``````

Special feature of tracer is tracing of `ASSERT` statement when `plpgsql_check.trace_assert` is `on`. When `plpgsql_check.trace_assert_verbosity` is `DEFAULT`, then all function's or procedure's variables are displayed when assert expression is false. When this configuration is `VERBOSE` then all variables from all plpgsql frames are displayed. This behaviour is independent on `plpgsql.check_asserts` value. It can be used, although the assertions are disabled in plpgsql runtime.

``````postgres=# set plpgsql_check.tracer to off;
postgres=# set plpgsql_check.trace_assert_verbosity TO verbose;

postgres=# do \$\$ begin perform fx(10,null, 'now', e'stěhule'); end; \$\$;
NOTICE:  #4 PLpgSQL assert expression (false) on line 12 of fx(integer) is false
NOTICE:   "a" => '10', "res" => null, "b" => '20'
NOTICE:  #2 PL/pgSQL function fx(integer,integer,date,text) line 1 at PERFORM
NOTICE:   "a" => '10', "b" => null, "c" => '2020-08-05', "d" => 'stěhule'
NOTICE:  #0 PL/pgSQL function inline_code_block line 1 at PERFORM
ERROR:  assertion failed
CONTEXT:  PL/pgSQL function fx(integer) line 12 at ASSERT
SQL statement "SELECT fx(a)"
PL/pgSQL function fx(integer,integer,date,text) line 1 at PERFORM
SQL statement "SELECT fx(10,null, 'now', e'stěhule')"
PL/pgSQL function inline_code_block line 1 at PERFORM

postgres=# set plpgsql.check_asserts to off;
SET
postgres=# do \$\$ begin perform fx(10,null, 'now', e'stěhule'); end; \$\$;
NOTICE:  #4 PLpgSQL assert expression (false) on line 12 of fx(integer) is false
NOTICE:   "a" => '10', "res" => null, "b" => '20'
NOTICE:  #2 PL/pgSQL function fx(integer,integer,date,text) line 1 at PERFORM
NOTICE:   "a" => '10', "b" => null, "c" => '2020-08-05', "d" => 'stěhule'
NOTICE:  #0 PL/pgSQL function inline_code_block line 1 at PERFORM
DO
``````

## Attention - SECURITY

Tracer prints content of variables or function arguments. For security definer function, this content can hold security sensitive data. This is reason why tracer is disabled by default and should be enabled only with super user rights `plpgsql_check.enable_tracer`.

Pragma

You can configure plpgsql_check behave inside checked function with "pragma" function. This is a analogy of PL/SQL or ADA language of PRAGMA feature. PLpgSQL doesn't support PRAGMA, but plpgsql_check detects function named `plpgsql_check_pragma` and get options from parameters of this function. These plpgsql_check options are valid to end of group of statements.

``````CREATE OR REPLACE FUNCTION test()
RETURNS void AS \$\$
BEGIN
...
-- for following statements disable check
PERFORM plpgsql_check_pragma('disable:check');
...
-- enable check again
PERFORM plpgsql_check_pragma('enable:check');
...
END;
\$\$ LANGUAGE plpgsql;
``````

The function `plpgsql_check_pragma` is immutable function that returns one. It is defined by `plpgsql_check` extension. You can declare alternative `plpgsql_check_pragma` function like:

``````CREATE OR REPLACE FUNCTION plpgsql_check_pragma(VARIADIC args[])
RETURNS int AS \$\$
SELECT 1
\$\$ LANGUAGE sql IMMUTABLE;
``````

Using pragma function in declaration part of top block sets options on function level too.

``````CREATE OR REPLACE FUNCTION test()
RETURNS void AS \$\$
DECLARE
aux int := plpgsql_check_pragma('disable:extra_warnings');
...
``````

Shorter syntax for pragma is supported too:

``````CREATE OR REPLACE FUNCTION test()
RETURNS void AS \$\$
DECLARE r record;
BEGIN
PERFORM 'PRAGMA:TYPE:r (a int, b int)';
PERFORM 'PRAGMA:TABLE: x (like pg_class)';
...
``````

## Supported pragmas

`echo:str` - print string (for testing)

`status:check`,`status:tracer`, `status:other_warnings`, `status:performance_warnings`, `status:extra_warnings`,`status:security_warnings`

`enable:check`,`enable:tracer`, `enable:other_warnings`, `enable:performance_warnings`, `enable:extra_warnings`,`enable:security_warnings`

`disable:check`,`disable:tracer`, `disable:other_warnings`, `disable:performance_warnings`, `disable:extra_warnings`,`disable:security_warnings`

`type:varname typename` or `type:varname (fieldname type, ...)` - set type to variable of record type

`table: name (column_name type, ...)` or `table: name (like tablename)` - create ephereal table

Pragmas `enable:tracer` and `disable:tracer`are active for Postgres 12 and higher

Compilation

You need a development environment for PostgreSQL extensions:

``````make clean
make install
``````

result:

``````[pavel@localhost plpgsql_check]\$ make USE_PGXS=1 clean
rm -f plpgsql_check.so   libplpgsql_check.a  libplpgsql_check.pc
rm -f plpgsql_check.o
rm -rf results/ regression.diffs regression.out tmp_check/ log/
[pavel@localhost plpgsql_check]\$ make USE_PGXS=1 all
clang -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fpic -I/usr/local/pgsql/lib/pgxs/src/makefiles/../../src/pl/plpgsql/src -I. -I./ -I/usr/local/pgsql/include/server -I/usr/local/pgsql/include/internal -D_GNU_SOURCE   -c -o plpgsql_check.o plpgsql_check.c
clang -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fpic -I/usr/local/pgsql/lib/pgxs/src/makefiles/../../src/pl/plpgsql/src -shared -o plpgsql_check.so plpgsql_check.o -L/usr/local/pgsql/lib -Wl,--as-needed -Wl,-rpath,'/usr/local/pgsql/lib',--enable-new-dtags
[pavel@localhost plpgsql_check]\$ su root
[root@localhost plpgsql_check]# make USE_PGXS=1 install
/usr/bin/mkdir -p '/usr/local/pgsql/lib'
/usr/bin/mkdir -p '/usr/local/pgsql/share/extension'
/usr/bin/mkdir -p '/usr/local/pgsql/share/extension'
/usr/bin/install -c -m 755  plpgsql_check.so '/usr/local/pgsql/lib/plpgsql_check.so'
/usr/bin/install -c -m 644 plpgsql_check.control '/usr/local/pgsql/share/extension/'
/usr/bin/install -c -m 644 plpgsql_check--0.9.sql '/usr/local/pgsql/share/extension/'
[root@localhost plpgsql_check]# exit
[pavel@localhost plpgsql_check]\$ make USE_PGXS=1 installcheck
/usr/local/pgsql/lib/pgxs/src/makefiles/../../src/test/regress/pg_regress --inputdir=./ --psqldir='/usr/local/pgsql/bin'    --dbname=pl_regression --load-language=plpgsql --dbname=contrib_regression plpgsql_check_passive plpgsql_check_active plpgsql_check_active-9.5
(using postmaster on Unix socket, default port)
============== dropping database "contrib_regression" ==============
DROP DATABASE
============== creating database "contrib_regression" ==============
CREATE DATABASE
ALTER DATABASE
============== installing plpgsql                     ==============
CREATE LANGUAGE
============== running regression test queries        ==============
test plpgsql_check_passive    ... ok
test plpgsql_check_active     ... ok
test plpgsql_check_active-9.5 ... ok

=====================
All 3 tests passed.
=====================
``````

## Compilation on Ubuntu

Sometimes successful compilation can require libicu-dev package (PostgreSQL 10 and higher - when pg was compiled with ICU support)

``````sudo apt install libicu-dev
``````

## Compilation plpgsql_check on Windows

You can check precompiled dll libraries http://okbob.blogspot.cz/2015/02/plpgsqlcheck-is-available-for-microsoft.html

or compile by self:

4. Build plpgsql_check.dll
5. Install plugin
6. copy `plpgsql_check.dll` to `PostgreSQL\14\lib`
7. copy `plpgsql_check.control` and `plpgsql_check--2.1.sql` to `PostgreSQL\14\share\extension`

## Checked on

• gcc on Linux (against all supported PostgreSQL)
• clang 3.4 on Linux (against PostgreSQL 10)
• for success regress tests the PostgreSQL 10 or higher is required

Compilation against PostgreSQL 10 requires libICU!

Licence

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Note

If you like it, send a postcard to address

``````Pavel Stehule
Skalice 12
256 01 Benesov u Prahy
Czech Republic
``````

I invite any questions, comments, bug reports, patches on mail address pavel.stehule@gmail.com

Author: okbob
Source Code: https://github.com/okbob/plpgsql_check

1661592007

## A Collection Of Swift Tips & Tricks That I've Shared on Twitter

⚠️ This list is no longer being updated. For my latest Swift tips, checkout the "Tips" section on Swift by Sundell.

## Swift tips & tricks ⚡️

One of the things I really love about Swift is how I keep finding interesting ways to use it in various situations, and when I do - I usually share them on Twitter. Here's a collection of all the tips & tricks that I've shared so far. Each entry has a link to the original tweet, if you want to respond with some feedback or question, which is always super welcome! 🚀

Also make sure to check out all of my other Swift content:

## #102 Making async tests faster and more stable

🚀 Here are some quick tips to make async tests faster & more stable:

• 😴 Avoid sleep() - use expectations instead
• ⏱ Use generous timeouts to avoid flakiness on CI
• 🧐 Put all assertions at the end of each test, not inside closures
``````// BEFORE:

class MentionDetectorTests: XCTestCase {
func testDetectingMention() {
let detector = MentionDetector()
let string = "This test was written by @johnsundell."

detector.detectMentions(in: string) { mentions in
XCTAssertEqual(mentions, ["johnsundell"])
}

sleep(2)
}
}

// AFTER:

class MentionDetectorTests: XCTestCase {
func testDetectingMention() {
let detector = MentionDetector()
let string = "This test was written by @johnsundell."

var mentions: [String]?
let expectation = self.expectation(description: #function)

detector.detectMentions(in: string) {
mentions = \$0
expectation.fulfill()
}

waitForExpectations(timeout: 10)
XCTAssertEqual(mentions, ["johnsundell"])
}
}
``````

For more on async testing, check out "Unit testing asynchronous Swift code".

## #101 Adding support for Apple Pencil double-taps

✍️ Adding support for the new Apple Pencil double-tap feature is super easy! All you have to do is to create a `UIPencilInteraction`, add it to a view, and implement one delegate method. Hopefully all pencil-compatible apps will soon adopt this.

``````let interaction = UIPencilInteraction()
interaction.delegate = self

extension ViewController: UIPencilInteractionDelegate {
func pencilInteractionDidTap(_ interaction: UIPencilInteraction) {
// Handle pencil double-tap
}
}
``````

For more on using this and other iPad Pro features, check out "Building iPad Pro features in Swift".

## #100 Combining values with functions

😎 Here's a cool function that combines a value with a function to return a closure that captures that value, so that it can be called without any arguments. Super useful when working with closure-based APIs and we want to use some of our properties without having to capture `self`.

``````func combine<A, B>(_ value: A, with closure: @escaping (A) -> B) -> () -> B {
return { closure(value) }
}

// BEFORE:

class ProductViewController: UIViewController {

buyButton.handler = { [weak self] in
guard let self = self else {
return
}

self.productManager.startCheckout(for: self.product)
}
}
}

// AFTER:

class ProductViewController: UIViewController {

}
}
``````

## #99 Dependency injection using functions

💉 When I'm only using a single function from a dependency, I love to inject that function as a closure, instead of having to create a protocol and inject the whole object. Makes dependency injection & testing super simple.

``````final class ArticleLoader {
typealias Networking = (Endpoint) -> Future<Data>

private let networking: Networking

init(networking: @escaping Networking = URLSession.shared.load) {
self.networking = networking
}

return networking(.latestArticles).decode()
}
}
``````

For more on this technique, check out "Simple Swift dependency injection with functions".

## #98 Using a custom exception handler

💥 It's cool that you can easily assign a closure as a custom `NSException` handler. This is super useful when building things in Playgrounds - since you can't use breakpoints - so instead of just `signal SIGABRT`, you'll get the full exception description if something goes wrong.

``````NSSetUncaughtExceptionHandler { exception in
print(exception)
}
``````

## #97 Using type aliases to give semantic meaning to primitives

❤️ I love that in Swift, we can use the type system to make our code so much more self-documenting - one way of doing so is to use type aliases to give the primitive types that we use a more semantic meaning.

``````extension List.Item {
// Using type aliases, we can give semantic meaning to the
// primitive types that we use, without having to introduce
// wrapper types.
typealias Index = Int
}

extension List {
enum Mutation {
// Our enum cases now become a lot more self-documenting,
// explain them.
case update(Item, Item.Index)
case remove(Item.Index)
}
}
``````

For more on self-documenting code, check out "Writing self-documenting Swift code".

## #96 Specializing protocols using constraints

🤯 A little late night prototyping session reveals that protocol constraints can not only be applied to extensions - they can also be added to protocol definitions!

This is awesome, since it lets us easily define specialized protocols based on more generic ones.

``````protocol Component {
associatedtype Container
}

// Protocols that inherit from other protocols can include
// constraints to further specialize them.
protocol ViewComponent: Component where Container == UIView {
associatedtype View: UIView
var view: View { get }
}

extension ViewComponent {
}
}
``````

For more on specializing protocols, check out "Specializing protocols in Swift".

## #95 Unwrapping an optional or throwing an error

📦 Here's a super handy extension on Swift's `Optional` type, which gives us a really nice API for easily unwrapping an optional, or throwing an error in case the value turned out to be `nil`:

``````extension Optional {
func orThrow(_ errorExpression: @autoclosure () -> Error) throws -> Wrapped {
switch self {
case .some(let value):
return value
case .none:
throw errorExpression()
}
}
}

let file = try loadFile(at: path).orThrow(MissingFileError())
``````

For more ways that optionals can be extended, check out "Extending optionals in Swift".

## #94 Testing code that uses static APIs

👩‍🔬 Testing code that uses static APIs can be really tricky, but there's a way that it can often be done - using Swift's first class function capabilities!

Instead of accessing that static API directly, we can inject the function we want to use, which enables us to mock it!

``````// BEFORE

func loadFriends(then handler: @escaping (Result<[Friend]>) -> Void) {
...
}
}
}

// AFTER

typealias Handler<T> = (Result<T>) -> Void

then handler: @escaping Handler<[Friend]>) {
...
}
}
}

// MOCKING IN TESTS

handler(.success(mockData))
}

...
}
``````

## #93 Matching multiple enum cases with associated values

🐾 Swift's pattern matching capabilities are so powerful! Two enum cases with associated values can even be matched and handled by the same switch case - which is super useful when handling state changes with similar data.

``````enum DownloadState {
case inProgress(progress: Double)
case paused(progress: Double)
case cancelled
case finished(Data)
}

switch state {
case .inProgress(let progress), .paused(let progress):
updateProgressView(with: progress)
case .cancelled:
showCancelledMessage()
case .finished(let data):
process(data)
}
}
``````

## #92 Multiline string literals

🅰 One really nice benefit of Swift multiline string literals - even for single lines of text - is that they don't require quotes to be escaped. Perfect when working with things like HTML, or creating a custom description for an object.

``````let html = highlighter.highlight("Array<String>")

XCTAssertEqual(html, """
<span class="type">Array</span>&lt;<span class="type">String</span>&gt;
""")
``````

## #91 Reducing sequences

💎 While it's very common in functional programming, the `reduce` function might be a bit of a hidden gem in Swift. It provides a super useful way to transform a sequence into a single value.

``````extension Sequence where Element: Equatable {
func numberOfOccurrences(of target: Element) -> Int {
return reduce(0) { result, element in
guard element == target else {
return result
}

return result + 1
}
}
}
``````

You can read more about transforming collections in "Transforming collections in Swift".

## #90 Avoiding manual Codable implementations

📦 When I use Codable in Swift, I want to avoid manual implementations as much as possible, even when there's a mismatch between my code structure and the JSON I'm decoding.

One way that can often be achieved is to use private data containers combined with computed properties.

``````struct User: Codable {
let name: String
let age: Int

var homeTown: String { return originPlace.name }

private let originPlace: Place
}

private extension User {
struct Place: Codable {
let name: String
}
}

extension User {
struct Container: Codable {
let user: User
}
}
``````

## #89 Using feature flags instead of feature branches

🚢 Instead of using feature branches, I merge almost all of my code directly into master - and then I use feature flags to conditionally enable features when they're ready. That way I can avoid merge conflicts and keep shipping!

``````extension ListViewController {
// Rather than having to keep maintaining a separate
// feature branch for a new feature, we can use a flag
// to conditionally turn it on.
guard FeatureFlags.searchEnabled else {
return
}

let resultsVC = SearchResultsViewController()
let searchVC = UISearchController(
searchResultsController: resultsVC
)

searchVC.searchResultsUpdater = resultsVC
}
}
``````

You can read more about feature flags in "Feature flags in Swift".

## #88 Lightweight data hierarchies using tuples

💾 Here I'm using tuples to create a lightweight hierarchy for my data, giving me a nice structure without having to introduce any additional types.

``````struct CodeSegment {
var tokens: (
previous: String?,
current: String
)

var delimiters: (
previous: Character?
next: Character?
)
}

handle(segment.tokens.current)
``````

You can read more about tuples in "Using tuples as lightweight types in Swift"

## #87 The rule of threes

3️⃣ Whenever I have 3 properties or local variables that share the same prefix, I usually try to extract them into their own method or type. That way I can avoid massive types & methods, and also increase readability, without falling into a "premature optimization" trap.

Before

``````public func generate() throws {
let contentFolder = try folder.subfolder(named: "content")

let articleFolder = try contentFolder.subfolder(named: "posts")
let articleProcessor = ContentProcessor(folder: articleFolder)
let articles = try articleProcessor.process()

...
}
``````

After

``````public func generate() throws {
let contentFolder = try folder.subfolder(named: "content")
let articles = try processArticles(in: contentFolder)
...
}

private func processArticles(in folder: Folder) throws -> [ContentItem] {
let folder = try folder.subfolder(named: "posts")
let processor = ContentProcessor(folder: folder)
return try processor.process()
}
``````

## #86 Useful Codable extensions

👨‍🔧 Here's two extensions that I always add to the `Encodable` & `Decodable` protocols, which for me really make the Codable API nicer to use. By using type inference for decoding, a lot of boilerplate can be removed when the compiler is already able to infer the resulting type.

``````extension Encodable {
func encoded() throws -> Data {
return try JSONEncoder().encode(self)
}
}

extension Data {
func decoded<T: Decodable>() throws -> T {
return try JSONDecoder().decode(T.self, from: self)
}
}

let data = try user.encoded()

// By using a generic type in the decoded() method, the
// compiler can often infer the type we want to decode
// from the current context.

// And if not, we can always supply the type, still making
// the call site read very nicely.
let otherUser = try data.decoded() as User
``````

## #85 Using shared UserDefaults suites

📦 `UserDefaults` is a lot more powerful than what it first might seem like. Not only can it store more complex values (like dates & dictionaries) and parse command line arguments - it also enables easy sharing of settings & lightweight data between apps in the same App Group.

``````let sharedDefaults = UserDefaults(suiteName: "my-app-group")!
let useDarkMode = sharedDefaults.bool(forKey: "dark-mode")

// This value is put into the shared suite.
sharedDefaults.set(true, forKey: "dark-mode")

// If you want to treat the shared settings as read-only (and add
// local overrides on top of them), you can simply add the shared
// suite to the standard UserDefaults.
let combinedDefaults = UserDefaults.standard

// This value is a local override, not added to the shared suite.
combinedDefaults.set(true, forKey: "app-specific-override")
``````

## #84 Custom UIView backing layers

🎨 By overriding `layerClass` you can tell UIKit what `CALayer` class to use for a `UIView`'s backing layer. That way you can reduce the amount of layers, and don't have to do any manual layout.

``````final class GradientView: UIView {
override class var layerClass: AnyClass { return CAGradientLayer.self }

var colors: (start: UIColor, end: UIColor)? {
didSet { updateLayer() }
}

private func updateLayer() {
let layer = self.layer as! CAGradientLayer
layer.colors = colors.map { [\$0.start.cgColor, \$0.end.cgColor] }
}
}
``````

## #83 Auto-Equatable enums with associated values

✅ That the compiler now automatically synthesizes Equatable conformances is such a huge upgrade for Swift! And the cool thing is that it works for all kinds of types - even for enums with associated values! Especially useful when using enums for verification in unit tests.

``````struct Article: Equatable {
let title: String
let text: String
}

struct User: Equatable {
let name: String
let age: Int
}

extension Navigator {
enum Destination: Equatable {
case profile(User)
case article(Article)
}
}

func testNavigatingToArticle() {
let article = Article(title: "Title", text: "Text")
controller.select(article)
XCTAssertEqual(navigator.destinations, [.article(article)])
}
``````

## #82 Defaults for associated types

🤝 Associated types can have defaults in Swift - which is super useful for types that are not easily inferred (for example when they're not used for a specific instance method or property).

``````protocol Identifiable {
associatedtype RawIdentifier: Codable = String

var id: Identifier<Self> { get }
}

struct User: Identifiable {
let id: Identifier<User>
let name: String
}

struct Group: Identifiable {
typealias RawIdentifier = Int

let id: Identifier<Group>
let name: String
}
``````

## #81 Creating a dedicated identifier type

🆔 If you want to avoid using plain strings as identifiers (which can increase both type safety & readability), it's really easy to create a custom Identifier type that feels just like a native Swift type, thanks to protocols!

More on this topic in "Type-safe identifiers in Swift".

``````struct Identifier: Hashable {
let string: String
}

extension Identifier: ExpressibleByStringLiteral {
init(stringLiteral value: String) {
string = value
}
}

extension Identifier: CustomStringConvertible {
var description: String {
return string
}
}

extension Identifier: Codable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
string = try container.decode(String.self)
}

func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(string)
}
}

struct Article: Codable {
let id: Identifier
let title: String
}

let article = Article(id: "my-article", title: "Hello world!")
``````

## #80 Assigning optional tuple members to variables

🙌 A really cool thing about using tuples to model the internal state of a Swift type, is that you can unwrap an optional tuple's members directly into local variables.

Very useful in order to group multiple optional values together for easy unwrapping & handling.

``````class ImageTransformer {
private var queue = [(image: UIImage, transform: Transform)]()

private func processNext() {
// When unwrapping an optional tuple, you can assign the members
// directly to local variables.
guard let (image, transform) = queue.first else {
return
}

let context = Context()
context.draw(image)
context.apply(transform)
...
}
}
``````

## #79 Struct convenience initializers

❤️ I love to structure my code using extensions in Swift. One big benefit of doing so when it comes to struct initializers, is that defining a convenience initializer doesn't remove the default one the compiler generates - best of both worlds!

``````struct Article {
let date: Date
var title: String
var text: String
}

extension Article {
init(title: String, text: String) {
self.init(date: Date(), title: title, text: text, comments: [])
}
}

let articleA = Article(title: "Best Cupcake Recipe", text: "...")

let articleB = Article(
date: Date(),
title: "Best Cupcake Recipe",
text: "...",
Comment(user: currentUser, text: "Yep, can confirm!")
]
)
``````

## #78 Usages of throwing functions

🏈 A big benefit of using throwing functions for synchronous Swift APIs is that the caller can decide whether they want to treat the return value as optional (`try?`) or required (`try`).

``````func loadFile(named name: String) throws -> File {
guard let url = urlForFile(named: name) else {
throw File.Error.missing
}

do {
let data = try Data(contentsOf: url)
return File(url: url, data: data)
} catch {
throw File.Error.invalidData(error)
}
}

let requiredFile = try loadFile(named: "AppConfig.json")

let optionalFile = try? loadFile(named: "UserSettings.json")
``````

## #77 Nested generic types

🐝 Types that are nested in generics automatically inherit their parent's generic types - which is super useful when defining accessory types (for things like states or outcomes).

``````struct Task<Input, Output> {
typealias Closure = (Input) throws -> Output

let closure: Closure
}

enum Result {
case success(Output)
case failure(Error)
}
}
``````

## #76 Equatable & Hashable structures

🤖 Now that the Swift compiler automatically synthesizes Equatable & Hashable conformances for value types, it's easier than ever to setup model structures with nested types that are all `Equatable`/`Hashable`!

``````typealias Value = Hashable & Codable

struct User: Value {
var name: String
var age: Int
var settings: Settings
}

extension User {
struct Settings: Value {
var itemsPerPage: Int
var theme: Theme
}
}

extension User.Settings {
enum Theme: String, Value {
case light
case dark
}
}
``````

## #75 Conditional conformances

🎉 Swift 4.1 is here! One of the key features it brings is conditional conformances, which lets you have a type only conform to a protocol under certain constraints.

``````protocol UnboxTransformable {
associatedtype RawValue

static func transform(_ value: RawValue) throws -> Self?
}

extension Array: UnboxTransformable where Element: UnboxTransformable {
typealias RawValue = [Element.RawValue]

static func transform(_ value: RawValue) throws -> [Element]? {
return try value.compactMap(Element.transform)
}
}
``````

I also have an article with lots of more info on conditional conformances here. Paul Hudson also has a great overview of all Swift 4.1 features here.

## #74 Generic type aliases

🕵️‍♀️ A cool thing about Swift type aliases is that they can be generic! Combine that with tuples and you can easily define simple generic types.

``````typealias Pair<T> = (T, T)

extension Game {
func calculateScore(for players: Pair<Player>) -> Int {
...
}
}
``````

## #73 Parsing command line arguments using UserDefaults

☑️ A really cool "hidden" feature of UserDefaults is that it contains any arguments that were passed to the app at launch!

Super useful both in Swift command line tools & scripts, but also to temporarily override a value when debugging iOS apps.

``````let defaults = UserDefaults.standard
let query = defaults.string(forKey: "query")
let resultCount = defaults.integer(forKey: "results")
``````

## #72 Using the & operator

👏 Swift's `&` operator is awesome! Not only can you use it to compose protocols, you can compose other types too! Very useful if you want to hide concrete types & implementation details.

``````protocol LoadableFromURL {
}

...
}
}

class ViewControllerFactory {
func makeContentViewController() -> UIViewController & LoadableFromURL {
return ContentViewController()
}
}
``````

## #71 Capturing multiple values in mocks

🤗 When capturing values in mocks, using an array (instead of just a single value) makes it easy to verify that only a certain number of values were passed.

Perfect for protecting against "over-calling" something.

``````class UserManagerTests: XCTestCase {
func testObserversCalledWhenUserFirstLogsIn() {
let manager = UserManager()

let observer = ObserverMock()

// First login, observers should be notified
let user = User(id: 123, name: "John")
XCTAssertEqual(observer.users, [user])

// If the same user logs in again, observers shouldn't be notified
XCTAssertEqual(observer.users, [user])
}
}

private extension UserManagerTests {
class ObserverMock: UserManagerObserver {
private(set) var users = [User]()

func userDidChange(to user: User) {
users.append(user)
}
}
}
``````

## #70 Reducing the need for mocks

👋 When writing tests, you don't always need to create mocks - you can create stubs using real instances of things like errors, URLs & UserDefaults.

Here's how to do that for some common tasks/object types in Swift:

``````// Create errors using NSError (#function can be used to reference the name of the test)
let error = NSError(domain: #function, code: 1, userInfo: nil)

// Create non-optional URLs using file paths
let url = URL(fileURLWithPath: "Some/URL")

// Reference the test bundle using Bundle(for:)
let bundle = Bundle(for: type(of: self))

// Create an explicit UserDefaults object (instead of having to use a mock)
let userDefaults = UserDefaults(suiteName: #function)

// Create queues to control/await concurrent operations
let queue = DispatchQueue(label: #function)
``````

For when you actually do need mocking, check out "Mocking in Swift".

## #69 Using "then" as an external parameter label for closures

⏱ I've started using "then" as an external parameter label for completion handlers. Makes the call site read really nicely (Because I do ❤️ conversational API design) regardless of whether trailing closure syntax is used or not.

``````protocol DataLoader {
// Adding type aliases to protocols can be a great way to
// reduce verbosity for parameter types.
typealias Handler = (Result<Data>) -> Void
associatedtype Endpoint

func loadData(from endpoint: Endpoint, then handler: @escaping Handler)
}

...
}

...
})
``````

## #68 Combining lazily evaluated sequences with the builder pattern

😴 Combining lazily evaluated sequences with builder pattern-like properties can lead to some pretty sweet APIs for configurable sequences in Swift.

Also useful for queries & other things you "build up" and then execute.

``````// Extension adding builder pattern-like properties that return
// a new sequence value with the given configuration applied
extension FileSequence {
var recursive: FileSequence {
var sequence = self
sequence.isRecursive = true
return sequence
}

var includingHidden: FileSequence {
var sequence = self
sequence.includeHidden = true
return sequence
}
}

// BEFORE

let files = folder.makeFileSequence(recursive: true, includeHidden: true)

// AFTER

let files = folder.files.recursive.includingHidden
``````

Want an intro to lazy sequences? Check out "Swift sequences: The art of being lazy".

## #67 Faster & more stable UI tests

My top 3 tips for faster & more stable UI tests:

📱 Reset the app's state at the beginning of every test.

🆔 Use accessibility identifiers instead of UI strings.

⏱ Use expectations instead of waiting time.

``````func testOpeningArticle() {
// Launch the app with an argument that tells it to reset its state
let app = XCUIApplication()
app.launchArguments.append("--uitesting")
app.launch()

// Check that the app is displaying an activity indicator
let activityIndicator = app.activityIndicator.element
XCTAssertTrue(activityIndicator.exists)

expectation(for: NSPredicate(format: "exists == 0"),
evaluatedWith: activityIndicator)

// Use a generous timeout in case the network is slow
waitForExpectations(timeout: 10)

// Tap the cell for the first article
app.tables.cells["Article.0"].tap()

// Assert that a label with the accessibility identifier "Article.Title" exists
let label = app.staticTexts["Article.Title"]
XCTAssertTrue(label.exists)
}
``````

## #66 Accessing the clipboard from a Swift script

📋 It's super easy to access the contents of the clipboard from a Swift script. A big benefit of Swift scripting is being able to use Cocoa's powerful APIs for Mac apps.

``````import Cocoa

let clipboard = NSPasteboard.general.string(forType: .string)
``````

## #65 Using tuples for view state

🎯 Using Swift tuples for view state can be a super nice way to group multiple properties together and render them reactively using the layout system.

By using a tuple we don't have to either introduce a new type or make our view model-aware.

``````class TextView: UIView {
var state: (title: String?, text: String?) {
// By telling UIKit that our view needs layout and binding our
// state in layoutSubviews, we can react to state changes without
// doing unnecessary layout work.
didSet { setNeedsLayout() }
}

private let titleLabel = UILabel()
private let textLabel = UILabel()

override func layoutSubviews() {
super.layoutSubviews()

titleLabel.text = state.title
textLabel.text = state.text

...
}
}
``````

## #64 Throwing tests and LocalizedError

⚾️ Swift tests can throw, which is super useful in order to avoid complicated logic or force unwrapping. By making errors conform to `LocalizedError`, you can also get a nice error message in Xcode if there's a failure.

``````class ImageCacheTests: XCTestCase {
let bundle = Bundle(for: type(of: self))
let cache = ImageCache(bundle: bundle)

// bundle using this UIImage initializer
let image = try require(UIImage(named: "sample", in: bundle, compatibleWith: nil))
try cache.cache(image, forKey: "key")

let cachedImage = try cache.image(forKey: "key")
XCTAssertEqual(image, cachedImage)
}
}

enum ImageCacheError {
case emptyKey
case dataConversionFailed
}

// When using throwing tests, making your errors conform to
// LocalizedError will render a much nicer error message in
// Xcode (per default only the error code is shown).
extension ImageCacheError: LocalizedError {
var errorDescription: String? {
switch self {
case .emptyKey:
return "An empty key was given"
case .dataConversionFailed:
return "Failed to convert the given image to Data"
}
}
}
``````

For more information, and the implementation of the `require` method used above, check out "Avoiding force unwrapping in Swift unit tests".

## #63 The difference between static and class properties

✍️ Unlike `static` properties, `class` properties can be overridden by subclasses (however, they can't be stored, only computed).

``````class TableViewCell: UITableViewCell {
class var preferredHeight: CGFloat { return 60 }
}

class TallTableViewCell: TableViewCell {
override class var preferredHeight: CGFloat { return 100 }
}
``````

## #62 Creating extensions with static factory methods

👨‍🎨 Creating extensions with static factory methods can be a great alternative to subclassing in Swift, especially for things like setting up UIViews, CALayers or other kinds of styling.

It also lets you remove a lot of styling & setup from your view controllers.

``````extension UILabel {
static func makeForTitle() -> UILabel {
let label = UILabel()
label.font = .boldSystemFont(ofSize: 24)
label.textColor = .darkGray
label.minimumScaleFactor = 0.75
return label
}

static func makeForText() -> UILabel {
let label = UILabel()
label.font = .systemFont(ofSize: 16)
label.textColor = .black
label.numberOfLines = 0
return label
}
}

class ArticleViewController: UIViewController {
lazy var titleLabel = UILabel.makeForTitle()
lazy var textLabel = UILabel.makeForText()
}
``````

## #61 Child view controller auto-resizing

🧒 An awesome thing about child view controllers is that they're automatically resized to match their parent, making them a super nice solution for things like loading & error views.

``````class ListViewController: UIViewController {

self?.handle(result)
}
}
}
``````

For more about child view controller (including the `add` and `remove` methods used above), check out "Using child view controllers as plugins in Swift".

## #60 Using zip

🤐 Using the zip function in Swift you can easily combine two sequences. Super useful when using two sequences to do some work, since zip takes care of all the bounds-checking.

``````func render(titles: [String]) {
for (label, text) in zip(titleLabels, titles) {
print(text)
label.text = text
}
}
``````

## #59 Defining custom option sets

🎛 The awesome thing about option sets in Swift is that they can automatically either be passed as a single member or as a set. Even cooler is that you can easily define your own option sets as well, perfect for options and other non-exclusive values.

``````// Option sets are awesome, because you can easily pass them
// both using dot syntax and array literal syntax, like when
// using the UIView animation API:
UIView.animate(withDuration: 0.3,
delay: 0,
options: .allowUserInteraction,
animations: animations)

UIView.animate(withDuration: 0.3,
delay: 0,
options: [.allowUserInteraction, .layoutSubviews],
animations: animations)

// The cool thing is that you can easily define your own option
// sets as well, by defining a struct that has an Int rawValue,
// that will be used as a bit mask.
extension Cache {
struct Options: OptionSet {
static let saveToDisk = Options(rawValue: 1)
static let clearOnMemoryWarning = Options(rawValue: 1 << 1)
static let clearDaily = Options(rawValue: 1 << 2)

let rawValue: Int
}
}

// We can now use Cache.Options just like UIViewAnimationOptions:
Cache(options: .saveToDisk)
Cache(options: [.saveToDisk, .clearDaily])
``````

## #58 Using the where clause with associated types

🙌 Using the `where` clause when designing protocol-oriented APIs in Swift can let your implementations (or others' if it's open source) have a lot more freedom, especially when it comes to collections.

``````public protocol PathFinderMap {
associatedtype Node
// Using the 'where' clause for associated types, we can
// ensure that a type meets certain requirements (in this
// case that it's a sequence with Node elements).
associatedtype NodeSequence: Sequence where NodeSequence.Element == Node

// Instead of using a concrete type (like [Node]) here, we
// give implementors of this protocol more freedom while
// still meeting our requirements. For example, one
// implementation might use Set<Node>.
func neighbors(of node: Node) -> NodeSequence
}
``````

## #57 Using first class functions when iterating over a dictionary

👨‍🍳 Combine first class functions in Swift with the fact that Dictionary elements are (Key, Value) tuples and you can build yourself some pretty awesome functional chains when iterating over a Dictionary.

``````func makeActor(at coordinate: Coordinate, for building: Building) -> Actor {
let actor = Actor()
actor.position = coordinate.point
actor.animation = building.animation
return actor
}

func render(_ buildings: [Coordinate : Building]) {
}
``````

## #56 Calling instance methods as static functions

😎 In Swift, you can call any instance method as a static function and it will return a closure representing that method. This is how running tests using SPM on Linux works.

``````// This produces a '() -> Void' closure which is a reference to the
// given view's 'removeFromSuperview' method.
let closure = UIView.removeFromSuperview(view)

// We can now call it just like we would any other closure, and it
// will run 'view.removeFromSuperview()'
closure()

// This is how running tests using the Swift Package Manager on Linux
// works, you return your test functions as closures:
extension UserManagerTests {
static var allTests = [
("testLoggingIn", testLoggingIn),
("testLoggingOut", testLoggingOut),
("testUserPermissions", testUserPermissions)
]
}
``````

## #55 Dropping suffixes from method names to support multiple arguments

👏 One really nice benefit of dropping suffixes from method names (and just using verbs, when possible) is that it becomes super easy to support both single and multiple arguments, and it works really well semantically.

``````extension UIView {
}
}

// By dropping the "Subview" suffix from the method name, both
// single and multiple arguments work really well semantically.
``````

## #54 Constraining protocols to classes to ensure mutability

👽 Using the `AnyObject` (or `class`) constraint on protocols is not only useful when defining delegates (or other weak references), but also when you always want instances to be mutable without copying.

``````// By constraining a protocol with 'AnyObject' it can only be adopted
// by classes, which means all instances will always be mutable, and
// that it's the original instance (not a copy) that will be mutated.
protocol DataContainer: AnyObject {
var data: Data? { get set }
}

class UserSettingsManager {
private var settings: Settings
private let dataContainer: DataContainer

// Since DataContainer is a protocol, we an easily mock it in
// tests if we use dependency injection
init(settings: Settings, dataContainer: DataContainer) {
self.settings = settings
self.dataContainer = dataContainer
}

func saveSettings() throws {
let data = try settings.serialize()

// We can now assign properties on an instance of our protocol
// because the compiler knows it's always going to be a class
dataContainer.data = data
}
}
``````

## #53 String-based enums in string interpolation

🍣 Even if you define a custom raw value for a string-based enum in Swift, the full case name will be used in string interpolation.

Super useful when using separate raw values for JSON, while still wanting to use the full case name in other contexts.

``````extension Building {
// This enum has custom raw values that are used when decoding
// a value, for example from JSON.
enum Kind: String {
case castle = "C"
case town = "T"
case barracks = "B"
case goldMine = "G"
case camp = "CA"
case blacksmith = "BL"
}

var animation: Animation {
return Animation(
// When used in string interpolation, the full case name is still used.
// For 'castle' this will be 'buildings/castle'.
name: "buildings/\(kind)",
frameCount: frameCount,
frameDuration: frameDuration
)
}
}
``````

## #52 Expressively comparing a value with a list of candidates

👨‍🔬 Continuing to experiment with expressive ways of comparing a value with a list of candidates in Swift. Adding an extension on Equatable is probably my favorite approach so far.

``````extension Equatable {
func isAny(of candidates: Self...) -> Bool {
return candidates.contains(self)
}
}

let isHorizontal = direction.isAny(of: .left, .right)
``````

See tip #35 for my previous experiment.

## #51 UIView bounds and transforms

📐 A really interesting side-effect of a `UIView`'s `bounds` being its rect within its own coordinate system is that transforms don't affect it at all. That's why it's usually a better fit than `frame` when doing layout calculations of subviews.

``````let view = UIView()
view.frame.size = CGSize(width: 100, height: 100)
view.transform = CGAffineTransform(scaleX: 2, y: 2)

print(view.frame) // (-50.0, -50.0, 200.0, 200.0)
print(view.bounds) // (0.0, 0.0, 100.0, 100.0)
``````

## #50 UIKit default arguments

👏 It's awesome that many UIKit APIs with completion handlers and other optional parameters import into Swift with default arguments (even though they are written in Objective-C). Getting rid of all those nil arguments is so nice!

``````// BEFORE: All parameters are specified, just like in Objective-C

viewController.present(modalViewController, animated: true, completion: nil)

modalViewController.dismiss(animated: true, completion: nil)

to: contentViewController,
duration: 0.3,
options: [],
animations: animations,
completion: nil)

// AFTER: Since many UIKit APIs with completion handlers and other
// optional parameters import into Swift with default arguments,
// we can make our calls shorter

viewController.present(modalViewController, animated: true)

modalViewController.dismiss(animated: true)

to: contentViewController,
duration: 0.3,
animations: animations)
``````

## #49 Avoiding Massive View Controllers

✂️ Avoiding Massive View Controllers is all about finding the right levels of abstraction and splitting things up.

My personal rule of thumb is that as soon as I have 3 methods or properties that have the same prefix, I break them out into their own type.

``````// BEFORE

private lazy var signUpLabel = UILabel()
private lazy var signUpImageView = UIImageView()
private lazy var signUpButton = UIButton()
}

// AFTER

private lazy var signUpView = SignUpView()
}

class SignUpView: UIView {
private lazy var label = UILabel()
private lazy var imageView = UIImageView()
private lazy var button = UIButton()
}
``````

## #48 Extending optionals

❤️ I love the fact that optionals are enums in Swift - it makes it so easy to extend them with convenience APIs for certain types. Especially useful when doing things like data validation on optional values.

``````func validateTextFields() -> Bool {
return false
}

...

return true
}

// Since all optionals are actual enum values in Swift, we can easily
// extend them for certain types, to add our own convenience APIs

extension Optional where Wrapped == String {
var isNilOrEmpty: Bool {
switch self {
case let string?:
return string.isEmpty
case nil:
return true
}
}
}

// Since strings are now Collections in Swift 4, you can even
// add this property to all optional collections:

extension Optional where Wrapped: Collection {
var isNilOrEmpty: Bool {
switch self {
case let collection?:
return collection.isEmpty
case nil:
return true
}
}
}
``````

## #47 Using where with for-loops

🗺 Using the `where` keyword can be a super nice way to quickly apply a filter in a `for`-loop in Swift. You can of course use `map`, `filter` and `forEach`, or `guard`, but for simple loops I think this is very expressive and nice.

``````func archiveMarkedPosts() {
for post in posts where post.isMarked {
archive(post)
}
}

func healAllies() {
for player in players where player.isAllied(to: currentPlayer) {
player.heal()
}
}
``````

👻 Variable shadowing can be super useful in Swift, especially when you want to create a local copy of a parameter value in order to use it as state within a closure.

``````init(repeatMode: RepeatMode, closure: @escaping () -> UpdateOutcome) {
// Shadow the argument with a local, mutable copy
var repeatMode = repeatMode

self.closure = {
// With shadowing, there's no risk of accidentially
// referring to the immutable version
switch repeatMode {
case .forever:
break
case .times(let count):
guard count > 0 else {
return .finished
}

// We can now capture the mutable version and use
// it for state in a closure
repeatMode = .times(count - 1)
}

return closure()
}
}
``````

## #45 Using dot syntax for static properties and initializers

✒️ Dot syntax is one of my favorite features of Swift. What's really cool is that it's not only for enums, any static method or property can be used with dot syntax - even initializers! Perfect for convenience APIs and default parameters.

``````public enum RepeatMode {
case times(Int)
case forever
}

public extension RepeatMode {
static var never: RepeatMode {
return .times(0)
}

static var once: RepeatMode {
return .times(1)
}
}

view.perform(animation, repeated: .once)

// To make default parameters more compact, you can even use init with dot syntax

init(cache: Cache = .init(), decoder: ImageDecoder = .init()) {
...
}
}
``````

## #44 Calling functions as closures with a tuple as parameters

🚀 One really cool aspect of Swift having first class functions is that you can pass any function (or even initializer) as a closure, and even call it with a tuple containing its parameters!

``````// This function lets us treat any "normal" function or method as
// a closure and run it with a tuple that contains its parameters
func call<Input, Output>(_ function: (Input) -> Output, with input: Input) -> Output {
return function(input)
}

class ViewFactory {
// We can now pass an initializer as a closure, and a tuple
// containing its parameters
}

private func loadTextStyles() -> (font: UIFont, color: UIColor) {
return (theme.font, theme.textColor)
}
}

init(font: UIFont, textColor: UIColor) {
...
}
}
``````

## #43 Enabling static dependency injection

💉 If you've been struggling to test code that uses static APIs, here's a technique you can use to enable static dependency injection without having to modify any call sites:

``````// Before: Almost impossible to test due to the use of singletons

class Analytics {
static func log(_ event: Event) {
Database.shared.save(event)

let dictionary = event.serialize()
NetworkManager.shared.post(dictionary, to: eventURL)
}
}

// After: Much easier to test, since we can inject mocks as arguments

class Analytics {
static func log(_ event: Event,
database: Database = .shared,
networkManager: NetworkManager = .shared) {
database.save(event)

let dictionary = event.serialize()
networkManager.post(dictionary, to: eventURL)
}
}
``````

## #42 Type inference for lazy properties in Swift 4

🎉 In Swift 4, type inference works for lazy properties and you don't need to explicitly refer to `self`!

``````// Swift 3

class PurchaseView: UIView {

private func makeBuyButton() -> UIButton {
let button = UIButton()
button.setTitleColor(.blue, for: .normal)
return button
}
}

// Swift 4

class PurchaseView: UIView {

private func makeBuyButton() -> UIButton {
let button = UIButton()
button.setTitleColor(.blue, for: .normal)
return button
}
}
``````

## #41 Converting Swift errors to NSError

😎 You can turn any Swift `Error` into an `NSError`, which is super useful when pattern matching with a code 👍. Also, switching on optionals is pretty cool!

``````let task = urlSession.dataTask(with: url) { data, _, error in
switch error {
case .some(let error as NSError) where error.code == NSURLErrorNotConnectedToInternet:
presenter.showOfflineView()
case .some(let error):
presenter.showGenericErrorView()
case .none:
presenter.renderContent(from: data)
}
}

``````

Also make sure to check out Kostas Kremizas' tip about how you can pattern match directly against a member of `URLError`.

## #40 Making UIImage macOS compatible

🖥 Here's an easy way to make iOS model code that uses `UIImage` macOS compatible - like me and Gui Rambo discussed on the Swift by Sundell Podcast.

``````// Either put this in a separate file that you only include in your macOS target or wrap the code in #if os(macOS) / #endif

import Cocoa

// Step 1: Typealias UIImage to NSImage
typealias UIImage = NSImage

// Step 2: You might want to add these APIs that UIImage has but NSImage doesn't.
extension NSImage {
var cgImage: CGImage? {
var proposedRect = CGRect(origin: .zero, size: size)

return cgImage(forProposedRect: &proposedRect,
context: nil,
hints: nil)
}

convenience init?(named name: String) {
self.init(named: Name(name))
}
}

// Step 3: Profit - you can now make your model code that uses UIImage cross-platform!
struct User {
let name: String
let profileImage: UIImage
}
``````

## #39 Internally mutable protocol-oriented APIs

🤖 You can easily define a protocol-oriented API that can only be mutated internally, by using an internal protocol that extends a public one.

``````// Declare a public protocol that acts as your immutable API
public protocol ModelHolder {
associatedtype Model
var model: Model { get }
}

// Declare an extended, internal protocol that provides a mutable API
internal protocol MutableModelHolder: ModelHolder {
var model: Model { get set }
}

// You can now implement the requirements using 'public internal(set)'
public class UserHolder: MutableModelHolder {
public internal(set) var model: User

internal init(model: User) {
self.model = model
}
}
``````

## #38 Switching on a set

🎛 You can switch on a set using array literals as cases in Swift! Can be really useful to avoid many `if`/`else if` statements.

``````class RoadTile: Tile {
var connectedDirections = Set<Direction>()

func render() {
switch connectedDirections {
case [.up, .down]:
case [.left, .right]:
default:
}
}
}
``````

## #37 Adding the current locale to cache keys

🌍 When caching localized content in an app, it's a good idea to add the current locale to all keys, to prevent bugs when switching languages.

``````func cache(_ content: Content, forKey key: String) throws {
let data = try wrap(content) as Data
let key = localize(key: key)
try storage.store(data, forKey: key)
}

func loadCachedContent(forKey key: String) -> Content? {
let key = localize(key: key)
return data.flatMap { try? unbox(data: \$0) }
}

private func localize(key: String) -> String {
return key + "-" + Bundle.main.preferredLocalizations[0]
}
``````

## #36 Setting up tests to avoid retain cycles with weak references

🚳 Here's an easy way to setup a test to avoid accidental retain cycles with object relationships (like weak delegates & observers) in Swift:

``````func testDelegateNotRetained() {
// Assign the delegate (weak) and also retain it using a local var
var delegate: Delegate? = DelegateMock()
controller.delegate = delegate
XCTAssertNotNil(controller.delegate)

// Release the local var, which should also release the weak reference
delegate = nil
XCTAssertNil(controller.delegate)
}
``````

## #35 Expressively matching a value against a list of candidates

👨‍🔬 Playing around with an expressive way to check if a value matches any of a list of candidates in Swift:

``````// Instead of multiple conditions like this:

if string == "One" || string == "Two" || string == "Three" {

}

// You can now do:

if string == any(of: "One", "Two", "Three") {

}
``````

You can find a gist with the implementation here.

## #34 Organizing code using extensions

👪 APIs in a Swift extension automatically inherit its access control level, making it a neat way to organize public, internal & private APIs.

``````public extension Animation {
init(textureNamed textureName: String) {
frames = [Texture(name: textureName)]
}

init(texturesNamed textureNames: [String], frameDuration: TimeInterval = 1) {
frames = textureNames.map(Texture.init)
self.frameDuration = frameDuration
}

init(image: Image) {
frames = [Texture(image: image)]
}
}

internal extension Animation {
}
}
``````

## #33 Using map to transform an optional into a Result type

🗺 Using `map` you can transform an optional value into an optional `Result` type by simply passing in the enum case.

``````enum Result<Value> {
case value(Value)
case error(Error)
}

class Promise<Value> {
private var result: Result<Value>?

init(value: Value? = nil) {
result = value.map(Result.value)
}
}
``````

## #32 Assigning to self in struct initializers

👌 It's so nice that you can assign directly to `self` in `struct` initializers in Swift. Very useful when adding conformance to protocols.

``````extension Bool: AnswerConvertible {
public init(input: String) throws {
switch input.lowercased() {
case "y", "yes", "👍":
self = true
default:
self = false
}
}
}
``````

## #31 Recursively calling closures as inline functions

☎️ Defining Swift closures as inline functions enables you to recursively call them, which is super useful in things like custom sequences.

``````class Database {
func records(matching query: Query) -> AnySequence<Record> {

func iterate() -> Record? {
guard let nextRecord = recordIterator.next() else {
return nil
}

guard nextRecord.matches(query) else {
// Since the closure is an inline function, it can be recursively called,
// in this case in order to advance to the next item.
return iterate()
}

return nextRecord
}

// AnySequence/AnyIterator are part of the standard library and provide an easy way
// to define custom sequences using closures.
return AnySequence { AnyIterator(iterate) }
}
}
``````

Rob Napier points out that using the above might cause crashes if used on a large databaset, since Swift has no guaranteed Tail Call Optimization (TCO).

Slava Pestov also points out that another benefit of inline functions vs closures is that they can have their own generic parameter list.

## #30 Passing self to required Objective-C dependencies

🏖 Using lazy properties in Swift, you can pass `self` to required Objective-C dependencies without having to use force-unwrapped optionals.

``````class DataLoader: NSObject {
lazy var urlSession: URLSession = self.makeURLSession()

private func makeURLSession() -> URLSession {
return URLSession(configuration: .default, delegate: self, delegateQueue: .main)
}
}

class Renderer {

}
}
``````

## #29 Making weak or lazy properties readonly

👓 If you have a property in Swift that needs to be `weak` or `lazy`, you can still make it readonly by using `private(set)`.

``````class Node {
private(set) weak var parent: Node?
private(set) lazy var children = [Node]()

children.append(child)
child.parent = self
}
}
``````

## #28 Defining static URLs using string literals

🌏 Tired of using `URL(string: "url")!` for static URLs? Make `URL` conform to `ExpressibleByStringLiteral` and you can now simply use `"url"` instead.

``````extension URL: ExpressibleByStringLiteral {
// By using 'StaticString' we disable string interpolation, for safety
public init(stringLiteral value: StaticString) {
self = URL(string: "\(value)").require(hint: "Invalid URL string literal: \(value)")
}
}

// We can now define URLs using static string literals 🎉
let url: URL = "https://www.swiftbysundell.com"

// In Swift 3 or earlier, you also have to implement 2 additional initializers
extension URL {
public init(extendedGraphemeClusterLiteral value: StaticString) {
self.init(stringLiteral: value)
}

public init(unicodeScalarLiteral value: StaticString) {
self.init(stringLiteral: value)
}
}
``````

To find the extension that adds the `require()` method on `Optional` that I use above, check out Require.

## #27 Manipulating points, sizes and frames using math operators

✚ I'm always careful with operator overloading, but for manipulating things like sizes, points & frames I find them super useful.

``````extension CGSize {
static func *(lhs: CGSize, rhs: CGFloat) -> CGSize {
return CGSize(width: lhs.width * rhs, height: lhs.height * rhs)
}
}

button.frame.size = image.size * 2
``````

If you like the above idea, check out CGOperators, which contains math operator overloads for all Core Graphics' vector types.

## #26 Using closure types in generic constraints

🔗 You can use closure types in generic constraints in Swift. Enables nice APIs for handling sequences of closures.

``````extension Sequence where Element == () -> Void {
func callAll() {
forEach { \$0() }
}
}

extension Sequence where Element == () -> String {
func joinedResults(separator: String) -> String {
return map { \$0() }.joined(separator: separator)
}
}

callbacks.callAll()
let names = nameProviders.joinedResults(separator: ", ")
``````

(If you're using Swift 3, you have to change `Element` to `Iterator.Element`)

## #25 Using associated enum values to avoid state-specific optionals

🎉 Using associated enum values is a super nice way to encapsulate mutually exclusive state info (and avoiding state-specific optionals).

``````// BEFORE: Lots of state-specific, optional properties

class Player {
var isWaitingForMatchMaking: Bool
var invitingUser: User?
var numberOfLives: Int
var playerDefeatedBy: Player?
var roundDefeatedIn: Int?
}

// AFTER: All state-specific information is encapsulated in enum cases

class Player {
enum State {
case waitingForMatchMaking
case waitingForInviteResponse(from: User)
case active(numberOfLives: Int)
case defeated(by: Player, roundNumber: Int)
}

var state: State
}
``````

## #24 Using enums for async result types

👍 I really like using enums for all async result types, even boolean ones. Self-documenting, and makes the call site a lot nicer to read too!

``````protocol PushNotificationService {
// Before
func enablePushNotifications(completionHandler: @escaping (Bool) -> Void)

// After
}

case enabled
case disabled
}

if status == .enabled {
}
}
``````

## #23 Working on async code in a playground

🏃 Want to work on your async code in a Swift Playground? Just set `needsIndefiniteExecution` to true to keep it running:

``````import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let greeting = "Hello after 3 seconds"
print(greeting)
}
``````

To stop the playground from executing, simply call `PlaygroundPage.current.finishExecution()`.

## #22 Overriding self with a weak reference

💦 Avoid memory leaks when accidentially refering to `self` in closures by overriding it locally with a weak reference:

Swift >= 4.2

``````dataLoader.loadData(from: url) { [weak self] result in
guard let self = self else {
return
}

self.cache(result)

...
``````

Swift < 4.2

``````dataLoader.loadData(from: url) { [weak self] result in
guard let `self` = self else {
return
}

self.cache(result)

...
``````

Note that the reason the above currently works is because of a compiler bug (which I hope gets turned into a properly supported feature soon).

## #21 Using DispatchWorkItem

🕓 Using dispatch work items you can easily cancel a delayed asynchronous GCD task if you no longer need it:

``````let workItem = DispatchWorkItem {
// Your async code goes in here
}

// Execute the work item after 1 second
DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: workItem)

// You can cancel the work item if you no longer need it
workItem.cancel()
``````

## #20 Combining a sequence of functions

➕ While working on a new Swift developer tool (to be open sourced soon 😉), I came up with a pretty neat way of organizing its sequence of operations, by combining their functions into a closure:

``````internal func +<A, B, C>(lhs: @escaping (A) throws -> B,
rhs: @escaping (B) throws -> C) -> (A) throws -> C {
return { try rhs(lhs(\$0)) }
}

public func run() throws {
try (determineTarget + build + analyze + output)()
}
``````

If you're familiar with the functional programming world, you might know the above technique as the pipe operator (thanks to Alexey Demedreckiy for pointing this out!)

## #19 Chaining optionals with map() and flatMap()

🗺 Using `map()` and `flatMap()` on optionals you can chain multiple operations without having to use lengthy `if lets` or `guards`:

``````// BEFORE

guard let string = argument(at: 1) else {
return
}

guard let url = URL(string: string) else {
return
}

handle(url)

// AFTER

argument(at: 1).flatMap(URL.init).map(handle)
``````

## #18 Using self-executing closures for lazy properties

🚀 Using self-executing closures is a great way to encapsulate lazy property initialization:

``````class StoreViewController: UIViewController {
private lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let view = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout)
view.delegate = self
view.dataSource = self
return view
}()

}
}
``````

## #17 Speeding up Swift package tests

⚡️ You can speed up your Swift package tests using the `--parallel` flag. For Marathon, the tests execute 3 times faster that way!

``````swift test --parallel
``````

## #16 Avoiding mocking UserDefaults

🛠 Struggling with mocking `UserDefaults` in a test? The good news is: you don't need mocking - just create a real instance:

``````class LoginTests: XCTestCase {
private var userDefaults: UserDefaults!

override func setUp() {
super.setup()

userDefaults = UserDefaults(suiteName: #file)
userDefaults.removePersistentDomain(forName: #file)

}
}
``````

👍 Using variadic parameters in Swift, you can create some really nice APIs that take a list of objects without having to use an array:

``````extension Canvas {
}
}

let circle = Circle(center: CGPoint(x: 5, y: 5), radius: 5)
let lineA = Line(start: .zero, end: CGPoint(x: 10, y: 10))
let lineB = Line(start: CGPoint(x: 0, y: 10), end: CGPoint(x: 10, y: 0))

let canvas = Canvas()
canvas.render()
``````

## #14 Referring to enum cases with associated values as closures

😮 Just like you can refer to a Swift function as a closure, you can do the same thing with enum cases with associated values:

``````enum UnboxPath {
case key(String)
case keyPath(String)
}

struct UserSchema {
static let name = key("name")
static let age = key("age")
static let posts = key("posts")

private static let key = UnboxPath.key
}
``````

## #13 Using the === operator to compare objects by instance

📈 The `===` operator lets you check if two objects are the same instance. Very useful when verifying that an array contains an instance in a test:

``````protocol InstanceEquatable: class, Equatable {}

extension InstanceEquatable {
static func ==(lhs: Self, rhs: Self) -> Bool {
return lhs === rhs
}
}

extension Enemy: InstanceEquatable {}

func testDestroyingEnemy() {
player.attack(enemy)
XCTAssertTrue(player.destroyedEnemies.contains(enemy))
}
``````

## #12 Calling initializers with dot syntax and passing them as closures

😎 Cool thing about Swift initializers: you can call them using dot syntax and pass them as closures! Perfect for mocking dates in tests.

``````class Logger {
private let storage: LogStorage
private let dateProvider: () -> Date

init(storage: LogStorage = .init(), dateProvider: @escaping () -> Date = Date.init) {
self.storage = storage
self.dateProvider = dateProvider
}

func log(event: Event) {
storage.store(event: event, date: dateProvider())
}
}
``````

## #11 Structuring UI tests as extensions on XCUIApplication

📱 Most of my UI testing logic is now categories on `XCUIApplication`. Makes the test cases really easy to read:

``````func testLoggingInAndOut() {
XCTAssertFalse(app.userIsLoggedIn)

app.launch()
XCTAssertTrue(app.userIsLoggedIn)

app.logout()
XCTAssertFalse(app.userIsLoggedIn)
}

func testDisplayingCategories() {
XCTAssertFalse(app.isDisplayingCategories)

app.launch()
app.goToCategories()
XCTAssertTrue(app.isDisplayingCategories)
}
``````

## #10 Avoiding default cases in switch statements

🙂 It’s a good idea to avoid “default” cases when switching on Swift enums - it’ll “force you” to update your logic when a new case is added:

``````enum State {
case loggedIn
case loggedOut
case onboarding
}

func handle(_ state: State) {
switch state {
case .loggedIn:
showMainUI()
case .loggedOut:
// Compiler error: Switch must be exhaustive
}
}
``````

## #9 Using the guard statement in many different scopes

💂 It's really cool that you can use Swift's 'guard' statement to exit out of pretty much any scope, not only return from functions:

``````// You can use the 'guard' statement to...

for string in strings {
// ...continue an iteration
guard shouldProcess(string) else {
continue
}

// ...or break it
guard !shouldBreak(for: string) else {
break
}

// ...or return
guard !shouldReturn(for: string) else {
return
}

// ..or throw an error
guard string.isValid else {
throw StringError.invalid(string)
}

// ...or exit the program
guard !shouldExit(for: string) else {
exit(1)
}
}
``````

## #8 Passing functions & operators as closures

❤️ Love how you can pass functions & operators as closures in Swift. For example, it makes the syntax for sorting arrays really nice!

``````let array = [3, 9, 1, 4, 6, 2]
let sorted = array.sorted(by: <)
``````

## #7 Using #function for UserDefaults key consistency

🗝 Here's a neat little trick I use to get UserDefault key consistency in Swift (#function expands to the property name in getters/setters). Just remember to write a good suite of tests that'll guard you against bugs when changing property names.

``````extension UserDefaults {
var onboardingCompleted: Bool {
get { return bool(forKey: #function) }
set { set(newValue, forKey: #function) }
}
}
``````

## #6 Using a name already taken by the standard library

📛 Want to use a name already taken by the standard library for a nested type? No problem - just use `Swift.` to disambiguate:

``````extension Command {
enum Error: Swift.Error {
case missing
case invalid(String)
}
}
``````

## #5 Using Wrap to implement Equatable

📦 Playing around with using Wrap to implement `Equatable` for any type, primarily for testing:

``````protocol AutoEquatable: Equatable {}

extension AutoEquatable {
static func ==(lhs: Self, rhs: Self) -> Bool {
let lhsData = try! wrap(lhs) as Data
let rhsData = try! wrap(rhs) as Data
return lhsData == rhsData
}
}
``````

## #4 Using typealiases to reduce the length of method signatures

📏 One thing that I find really useful in Swift is to use typealiases to reduce the length of method signatures in generic types:

``````public class PathFinder<Object: PathFinderObject> {
public typealias Map = Object.Map
public typealias Node = Map.Node
public typealias Path = PathFinderPath<Object>

public static func possiblePaths(for object: Object, at rootNode: Node, on map: Map) -> Path.Sequence {
return .init(object: object, rootNode: rootNode, map: map)
}
}
``````

## #3 Referencing either external or internal parameter name when writing docs

📖 You can reference either the external or internal parameter label when writing Swift docs - and they get parsed the same:

``````// EITHER:

class Foo {
/**
*   - parameter string: A string
*/
func bar(with string: String) {}
}

// OR:

class Foo {
/**
*   - parameter with: A string
*/
func bar(with string: String) {}
}
``````

## #2 Using auto closures

👍 Finding more and more uses for auto closures in Swift. Can enable some pretty nice APIs:

``````extension Dictionary {
mutating func value(for key: Key, orAdd valueClosure: @autoclosure () -> Value) -> Value {
if let value = self[key] {
return value
}

let value = valueClosure()
self[key] = value
return value
}
}
``````

## #1 Namespacing with nested types

🚀 I’ve started to become a really big fan of nested types in Swift. Love the additional namespacing it gives you!

``````public struct Map {
public struct Model {
public let size: Size
public let theme: Theme
public var terrain: [Position : Terrain.Model]
public var units: [Position : Unit.Model]
public var buildings: [Position : Building.Model]
}

public enum Direction {
case up
case right
case down
case left
}

public struct Position {
public var x: Int
public var y: Int
}

public enum Size: String {
case small = "S"
case medium = "M"
case large = "L"
case extraLarge = "XL"
}
}``````

Author: JohnSundell
Source code: https://github.com/JohnSundell/SwiftTips

#swift

1642390128

## 파이썬 코딩 무료 강의 (활용편6) - 이미지 처리, 얼굴 인식을 통한 캐릭터 씌우기를 해보아요

파이썬 무료 강의 (활용편6 - 이미지 처리)입니다.
OpenCV 를 이용한 다양한 이미지 처리 기법과 재미있는 프로젝트를 진행합니다.
누구나 볼 수 있도록 쉽고 재미있게 제작하였습니다. ^^

[소개]
(0:00:00) 0.Intro
(0:00:31) 1.소개
(0:02:18) 2.활용편 6 이미지 처리 소개

[OpenCV 전반전]
(0:04:36) 3.환경설정
(0:08:41) 4.이미지 출력
(0:21:51) 5.동영상 출력 #1 파일
(0:29:58) 6.동영상 출력 #2 카메라
(0:34:23) 7.도형 그리기 #1 빈 스케치북
(0:39:49) 8.도형 그리기 #2 영역 색칠
(0:42:26) 9.도형 그리기 #3 직선
(0:51:23) 10.도형 그리기 #4 원
(0:55:09) 11.도형 그리기 #5 사각형
(0:58:32) 12.도형 그리기 #6 다각형
(1:09:23) 13.텍스트 #1 기본
(1:17:45) 14.텍스트 #2 한글 우회
(1:24:14) 15.파일 저장 #1 이미지
(1:29:27) 16.파일 저장 #2 동영상
(1:39:29) 17.크기 조정
(1:50:16) 18.이미지 자르기
(1:57:03) 19.이미지 대칭
(2:01:46) 20.이미지 회전
(2:06:07) 21.이미지 변형 - 흑백
(2:11:25) 22.이미지 변형 - 흐림
(2:18:03) 23.이미지 변형 - 원근 #1
(2:27:45) 24.이미지 변형 - 원근 #2

[반자동 문서 스캐너 프로젝트]
(2:32:50) 25.미니 프로젝트 1 - #1 마우스 이벤트 등록
(2:42:06) 26.미니 프로젝트 1 - #2 기본 코드 완성
(2:49:54) 27.미니 프로젝트 1 - #3 지점 선 긋기
(2:55:24) 28.미니 프로젝트 1 - #4 실시간 선 긋기

[OpenCV 후반전]
(3:01:52) 29.이미지 변형 - 이진화 #1 Trackbar
(3:14:37) 30.이미지 변형 - 이진화 #2 임계값
(3:20:26) 31.이미지 변형 - 이진화 #3 Adaptive Threshold
(3:28:34) 32.이미지 변형 - 이진화 #4 오츠 알고리즘
(3:32:22) 33.이미지 변환 - 팽창
(3:41:10) 34.이미지 변환 - 침식
(3:45:56) 35.이미지 변환 - 열림 & 닫힘
(3:54:10) 36.이미지 검출 - 경계선
(4:05:08) 37.이미지 검출 - 윤곽선 #1 기본
(4:15:26) 38.이미지 검출 - 윤곽선 #2 찾기 모드
(4:20:46) 39.이미지 검출 - 윤곽선 #3 면적

[카드 검출 & 분류기 프로젝트]
(4:27:42) 40.미니프로젝트 2

[퀴즈]
(4:31:57) 41.퀴즈

[얼굴인식 프로젝트]
(4:41:25) 42.환경설정 및 기본 코드 정리
(4:54:48) 43.눈과 코 인식하여 도형 그리기
(5:10:42) 44.그림판 이미지 씌우기
(5:20:52) 45.캐릭터 이미지 씌우기
(5:33:10) 46.보충설명
(5:40:53) 47.마치며 (학습 참고 자료)
(5:42:18) 48.Outro

[학습자료]
수업에 필요한 이미지, 동영상 자료 링크입니다.

고양이 이미지 : https://pixabay.com/images/id-2083492/
크기 : 640 x 390
파일명 : img.jpg

고양이 동영상 : https://www.pexels.com/video/7515833/
크기 : SD (360 x 640)
파일명 : video.mp4

신문 이미지 : https://pixabay.com/images/id-350376/
크기 : 1280 x 853
파일명 : newspaper.jpg

카드 이미지 1 : https://pixabay.com/images/id-682332/
크기 : 1280 x 1019
파일명 : poker.jpg

책 이미지 : https://www.pexels.com/photo/1029807/
크기 : Small (640 x 853)
파일명 : book.jpg

눈사람 이미지 : https://pixabay.com/images/id-1300089/
크기 : 1280 x 904
파일명 : snowman.png

카드 이미지 2 : https://pixabay.com/images/id-161404/
크기 : 640 x 408
파일명 : card.png

퀴즈용 동영상 : https://www.pexels.com/video/3121459/
크기 : HD (1280 x 720)
파일명 : city.mp4

프로젝트용 동영상 : https://www.pexels.com/video/3256542/
크기 : Full HD (1920 x 1080)
파일명 : face_video.mp4