Some databases rely heavily on their programming environment to deliver an imperative programming environment to where a developer can use high-level programming concepts such as control selection, iteration and flow.
Executing an anonymous code block is achieved with the DO keyword. It emulates setting up any other function, with no parameters and no return value. Because you have a function body, you are afforded the ability to use a DECLARE section to house variables.
DO$$BEGINEND$$;
Adding variables to this block:
DO$$DECLAREageINT;BEGINage:=24;IFage<18THEN-- handle the below 18 caseENDIF;END$$;
Because of the parameter and return value constraints on anonymous code blocks, getting information out can be a little tricky. There are some ways that we can get some information out though.
RAISE NOTICE allows us to present information to the console. This is considerably handy when we’re testing a block of code.
Of course, if your code is a re-usable block you should be creating a full function for it. This takes care of any input/output parameter issues as you take full control.
Looking elsewhere
If this isn’t to your liking, you can try any of the other operating environments/languages available:
Understanding what your database is doing and when is essential to runtime administration, maintenance, monitoring and reporting. Gaining insight into how your system responds to different workloads can also tell you how your current deployment is or isn’t serving your purpose.
There are manygreatarticles on this particular topic already. In today’s post, I’m going to walk through a couple of the simple things you can do to check your system’s runtime status.
Unix Tools
When your database is being hosted in a unix-like environment, you’re given the greatest tools at your disposal to understand what’s happening.
ps can show you running processes on your system. Paired with grep, you can focus ps to look at postgres processes only:
iostat and vmstat will also give you some operating system level insight to how your database application is performing.
Statistics Collector
An important, integrated piece of the Postgres architecture is the statistics collector. Using this, you can query to a very low level many pieces of information surrounding your system’s performance.
The following except is just a small sample of all of the views offered by the statistics collector; which are made available to the developer.
View Name
Description
pg_stat_activity
One row per server process, showing information related to the current activity of that process, such as state and current query. See pg_stat_activity for details.
pg_stat_bgwriter
One row only, showing statistics about the background writer process’s activity. See pg_stat_bgwriter for details.
pg_stat_database
One row per database, showing database-wide statistics. See pg_stat_database for details.
pg_stat_all_tables
One row for each table in the current database, showing statistics about accesses to that specific table. See pg_stat_all_tables for details.
pg_stat_sys_tables
Same as pg_stat_all_tables, except that only system tables are shown.
pg_stat_user_tables
Same as pg_stat_all_tables, except that only user tables are shown.
Scala gives the developer flexibility when reasoning about and designing type systems for applications. By using classes and traits, a developer can quickly build a complex hierarchy that can assist in describing constraint and relationship information.
In today’s post, I’m going to walk through a useless but demonstrative example of a type hierarchy and some of the constraint features available to the developer.
Vehicles
We’re going to model some different vehicles. Cars, planes, trucks, skateboards, whatever.
abstractclassVehicle
We could start case-classing this base out or directly adding derivatives that specialise down to the exact vehicle types that we want, but we’re going to reason about some attributes that these vehicles might have. Wheels and Jets.
When a vehicle HasWheels, the type is going to require us to specify numberOfWheels. Likewise numberOfJets for HasJets. These traits are extending our abstract Vehicle class.
When we have wheels, we should be able to set how fast they’re spinning.
Of course, we could have just constructed toyota as a MotorVehicle for the same effect. This just demonstrates the instance construction flexibility.
Constraints
Finally, when you’re writing functions that work with your types you can specify rich constraint rules so that you can target functionality with as much precision as you require:
// everything can be painteddefpaint(v:Vehicle)={}// only a vehicle with wheels can burnoutdefdoBurnout(v:VehiclewithHasWheels)={}
As you can see, you not only use the with keyword to define your types; this keyword is also used for variable construction and function signature definition.
When creating parameterised types, you have control on how those types can be passed. These nuances are referred to as variance and scala allows you to explicitly nominate how this works in your own classes.
An excellent explanation on these terms can be found here. I’ve reproduced the three main points for this article though:
That is, if A and B are types, f is a type transformation, and ≤ the subtype relation (i.e. A ≤ B means that A is a subtype of B), we have:
f is covariant if A ≤ B implies that f(A) ≤ f(B)
f is contravariant if A ≤ B implies that f(B) ≤ f(A)
f is invariant if neither of the above holds
Invariant
Invariant parameter types are what ensures that you can only pass MyContainer[Int] to def fn(x: MyContainer[Int]). The guarantee is that the type that you’re containing (when it’s being accessed) is done so as the correct type.
classMyInvariant[T](varvalue:T)
This guarantees the type of T when we go to work on it.
defdouble(a:MyInvariant[Int])={a.value*=2}
You can see here that a good case for invariant is for mutable data.
To show the error case here, we define a show function specialising to MyInvariant[Any]
defshow(a:MyInvariant[Any])={println("Here is: "+a.value)}
Trying to use this function:
scala> show(new MyInvariant[Int](5))
<console>:13: error: type mismatch;
found : MyInvariant[Int]
required: MyInvariant[Any]
Note: Int <: Any, but class MyInvariant is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
show(new MyInvariant[Int](5))
^
Covariant
Covariant parameter type is specific. You pass these sorts of types to functions that generalise their inner type access. You need to decorate the type parameter with a +.
classCovariantContainer[+T](varvalue:T)
Then your function to generalise over this type:
defshow(a:CovariantContainer[Any])={println("The value is "+a.value)}
Covariance is a good case for read-only scenarios.
Contravariant
Contravariance is defined by decorating the type parameter with a -. It’s useful in write-only situations.
classContravariantContainer[-T](varvalue:T)
We write specialised functions for the type, but that are write-only cases: