Skip to content

Commit

Permalink
feat(spanner): add PG.OID support (#1948)
Browse files Browse the repository at this point in the history
* feat: add PG.OID type cod annotation

PiperOrigin-RevId: 577053414

Source-Link: googleapis/googleapis@727c286

Source-Link: googleapis/googleapis-gen@2015275
Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiMjAxNTI3NWE3ZGRhMmFkM2QxNjA5ZjA2YzQyMDgxMjVjN2RlOGE5ZCJ9

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

* feat(spanner): add PG.OID support

* Adding system tests for PG.OID

* lint fixes

* fixes to address review feedback

---------

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
  • Loading branch information
skuruppu and gcf-owl-bot[bot] committed Feb 14, 2024
1 parent 6ef44c3 commit cf9df7a
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 0 deletions.
35 changes: 35 additions & 0 deletions src/codec.ts
Expand Up @@ -249,6 +249,25 @@ export class PGJsonb {
}
}

/**
* @typedef PGOid
* @see Spanner.pgOid
*/
export class PGOid extends WrappedNumber {
value: string;
constructor(value: string) {
super();
this.value = value.toString();
}
valueOf(): number {
const num = Number(this.value);
if (num > Number.MAX_SAFE_INTEGER) {
throw new GoogleError(`PG.OID ${this.value} is out of bounds.`);
}
return num;
}
}

/**
* @typedef JSONOptions
* @property {boolean} [wrapNumbers=false] Indicates if the numbers should be
Expand Down Expand Up @@ -364,6 +383,14 @@ function decode(value: Value, type: spannerClient.spanner.v1.Type): Value {
break;
case spannerClient.spanner.v1.TypeCode.INT64:
case 'INT64':
if (
type.typeAnnotation ===
spannerClient.spanner.v1.TypeAnnotationCode.PG_OID ||
type.typeAnnotation === 'PG_OID'
) {
decoded = new PGOid(decoded);
break;
}
decoded = new Int(decoded);
break;
case spannerClient.spanner.v1.TypeCode.NUMERIC:
Expand Down Expand Up @@ -503,6 +530,7 @@ const TypeCode: {
unspecified: 'TYPE_CODE_UNSPECIFIED',
bool: 'BOOL',
int64: 'INT64',
pgOid: 'INT64',
float64: 'FLOAT64',
numeric: 'NUMERIC',
pgNumeric: 'NUMERIC',
Expand Down Expand Up @@ -593,6 +621,10 @@ function getType(value: Value): Type {
return {type: 'pgJsonb'};
}

if (value instanceof PGOid) {
return {type: 'pgOid'};
}

if (is.boolean(value)) {
return {type: 'bool'};
}
Expand Down Expand Up @@ -733,6 +765,8 @@ function createTypeObject(
spannerClient.spanner.v1.TypeAnnotationCode.PG_NUMERIC;
} else if (friendlyType.type === 'jsonb') {
type.typeAnnotation = spannerClient.spanner.v1.TypeAnnotationCode.PG_JSONB;
} else if (friendlyType.type === 'pgOid') {
type.typeAnnotation = spannerClient.spanner.v1.TypeAnnotationCode.PG_OID;
}
return type;
}
Expand All @@ -748,6 +782,7 @@ export const codec = {
Numeric,
PGNumeric,
PGJsonb,
PGOid,
convertFieldsToJson,
decode,
encode,
Expand Down
78 changes: 78 additions & 0 deletions system-test/spanner.ts
Expand Up @@ -827,6 +827,38 @@ describe('Spanner', () => {
});
});

describe('oids', () => {
it('POSTGRESQL should read non-null pgOid values', function (done) {
if (IS_EMULATOR_ENABLED) {
this.skip();
}
PG_DATABASE.run('SELECT 123::oid', (err, rows) => {
assert.ifError(err);
let queriedValue = rows[0][0].value;
if (rows[0][0].value) {
queriedValue = rows[0][0].value.value;
}
assert.strictEqual(queriedValue, '123');
done();
});
});

it('POSTGRESQL should read null pgOid values', function (done) {
if (IS_EMULATOR_ENABLED) {
this.skip();
}
PG_DATABASE.run('SELECT null::oid', (err, rows) => {
assert.ifError(err);
let queriedValue = rows[0][0].value;
if (rows[0][0].value) {
queriedValue = rows[0][0].value.value;
}
assert.strictEqual(queriedValue, null);
done();
});
});
});

describe('float64s', () => {
const float64Insert = (done, dialect, value) => {
insert({FloatValue: value}, dialect, (err, row) => {
Expand Down Expand Up @@ -4951,6 +4983,52 @@ describe('Spanner', () => {
});
});

describe('pgOid', () => {
const oidQuery = (done, database, query, value) => {
database.run(query, (err, rows) => {
assert.ifError(err);
let queriedValue = rows[0][0].value;
if (rows[0][0].value) {
queriedValue = rows[0][0].value.value;
}
assert.strictEqual(queriedValue, value);
done();
});
};

it('POSTGRESQL should bind the value', function (done) {
if (IS_EMULATOR_ENABLED) {
this.skip();
}
const query = {
sql: 'SELECT $1',
params: {
p1: 1234,
},
types: {
v: 'pgOid',
},
};
oidQuery(done, PG_DATABASE, query, '1234');
});

it('POSTGRESQL should allow for null values', function (done) {
if (IS_EMULATOR_ENABLED) {
this.skip();
}
const query = {
sql: 'SELECT $1',
params: {
p1: null,
},
types: {
p1: 'pgOid',
},
};
oidQuery(done, PG_DATABASE, query, null);
});
});

describe('float64', () => {
const float64Query = (done, database, query, value) => {
database.run(query, (err, rows) => {
Expand Down
73 changes: 73 additions & 0 deletions test/codec.ts
Expand Up @@ -192,6 +192,35 @@ describe('codec', () => {
});
});

describe('PGOid', () => {
it('should stringify the value', () => {
const value = 8;
const oid = new codec.PGOid(value);

assert.strictEqual(oid.value, '8');
});

it('should return as a number', () => {
const value = 8;
const oid = new codec.PGOid(value);

assert.strictEqual(oid.valueOf(), 8);
assert.strictEqual(oid + 2, 10);
});

it('should throw if number is out of bounds', () => {
const value = '9223372036854775807';
const oid = new codec.PGOid(value);

assert.throws(
() => {
oid.valueOf();
},
new RegExp('PG.OID ' + value + ' is out of bounds.')
);
});
});

describe('Numeric', () => {
it('should store value as a string', () => {
const value = '8.01911';
Expand Down Expand Up @@ -601,6 +630,18 @@ describe('codec', () => {
assert.deepStrictEqual(decoded.toString(), expected);
});

it('should decode PG OID', () => {
const value = '64';

const decoded = codec.decode(value, {
code: google.spanner.v1.TypeCode.INT64,
typeAnnotation: google.spanner.v1.TypeAnnotationCode.PG_OID,
});

assert(decoded instanceof codec.PGOid);
assert.strictEqual(decoded.value, value);
});

it('should decode TIMESTAMP', () => {
const value = new Date();
const expected = new PreciseDate(value.getTime());
Expand Down Expand Up @@ -821,6 +862,14 @@ describe('codec', () => {
assert.strictEqual(encoded, '10');
});

it('should encode PG OID', () => {
const value = new codec.PGOid(10);

const encoded = codec.encode(value);

assert.strictEqual(encoded, '10');
});

it('should encode FLOAT64', () => {
const value = new codec.Float(10);

Expand Down Expand Up @@ -986,6 +1035,12 @@ describe('codec', () => {
type: 'pgNumeric',
});
});

it('should determine if the value is a PGOid', () => {
assert.deepStrictEqual(codec.getType(new codec.PGOid(5678)), {
type: 'pgOid',
});
});
});

describe('convertToListValue', () => {
Expand Down Expand Up @@ -1211,5 +1266,23 @@ describe('codec', () => {
typeAnnotation: google.spanner.v1.TypeAnnotationCode.PG_NUMERIC,
});
});

it('should set code and typeAnnotation for pgOid string', () => {
const type = codec.createTypeObject('pgOid');

assert.deepStrictEqual(type, {
code: google.spanner.v1.TypeCode[google.spanner.v1.TypeCode.INT64],
typeAnnotation: google.spanner.v1.TypeAnnotationCode.PG_OID,
});
});

it('should set code and typeAnnotation for pgOid friendlyType object', () => {
const type = codec.createTypeObject({type: 'pgOid'});

assert.deepStrictEqual(type, {
code: google.spanner.v1.TypeCode[google.spanner.v1.TypeCode.INT64],
typeAnnotation: google.spanner.v1.TypeAnnotationCode.PG_OID,
});
});
});
});

0 comments on commit cf9df7a

Please sign in to comment.