The ESENT API has existed for a while, so there are quite a few oddities to it.

After creating a database with ESENT, you need to close the returned JET_TABLEID.

The original way to create a usable table was to create the table with JetCreateTable, add some columns with repeated calls to JetAddColumn, then index the columns with JetCreateIndex. But in a multi-threaded concurrent program, you wouldn't want two threads to try and do the same thing, so the returned JET_TABLEID from JetCreateTable() is opened with exclusive access. No other JET_SESID can open that table.

Fast-forward to the introduction of JetCreateTableColumnIndex (and later JetCreateTableColumnIndex3, and in Windows 8 JetCreateTableColumnIndex4 [not yet on MSDN]). This API takes a large JET_TABLECREATE structure that holds the information needed for the table, column, and indices. Everything can be done with a single API call, without a need to do a bunch of loops.

But take a closer look at the JET_TABLECREATE structure:

typedef struct tagJET_TABLECREATE {
unsigned long cbStruct;
tchar* szTableName;
tchar* szTemplateTableName;
unsigned long ulPages;
unsigned long ulDensity;
JET_COLUMNCREATE* rgcolumncreate;
unsigned long cColumns;
JET_INDEXCREATE* rgindexcreate;
unsigned long cIndexes;
JET_GRBIT grbit;
JET_TABLEID tableid;
unsigned long cCreated;
} JET_TABLECREATE;

There are two output variables there: tableid and cCreated. cCreated returns the number of objects returned (e.g. when create a table with 5 columns and 3 indices, cCreated will be 1+5+3). This helps narrow down which table/column/index had a problem if the API failed. tableid is the JET_TABLEID of the newly created table.

We don't like to have multiple implementations of the code lying around, so internally JetCreateTable() will call the same function as JetCreateTableColumnIndex. The tableid was opened exclusively, to be just like the JetCreateTable/JetAddColumn/JetCreateIndex combination, it was also opened exclusively. So now it should be closed with JetCloseTable().